当前位置:网站首页>Go unit test mocks the database CRUD
Go unit test mocks the database CRUD
2022-06-21 21:47:00 【1024 Q】
Preface
go-sqlmock
install
Examples of use
miniredis
install
Examples of use
summary
PrefaceRecently, in practice, I have also summarized how to use... In a table driven way gock Mock Test external interface calls . And how GORM do mock test , After learning the basics , Later, I will write a separate article to introduce .
This is a Go Part of the language unit testing series 3 piece , How to use go-sqlmock and miniredis Tool execution MySQL and Redis Of mock test .
In the last article 《Go unit testing -- Simulate service requests and interface returns 》 in , We introduced how to use httptest and gock Tools for network testing .
In addition to network dependence , We often use various databases in our development , For example, the common MySQL and Redis etc. . This article will give examples to demonstrate how to write unit tests for MySQL and Redis Conduct mock.
go-sqlmocksqlmock Is an implementation sql/driver Of mock library . It does not need to establish a real database connection to simulate any sql Driver behavior . It is very convenient to use it when writing unit tests mock sql Statement execution result .
go get github.com/DATA-DOG/go-sqlmock Examples of use What we use here is go-sqlmock The basic sample code provided in the official document . In the following code , We have achieved a recordStats The function is used to record the relevant data generated when a user browses a product . The specific function is to perform the following two transactions in a transaction SQL operation :
The number of views of the current product in the table +1
stay product_viewers The user browsing the current product is recorded in the table id
// app.gopackage mainimport "database/sql"// recordStats Record user browsing product information func recordStats(db *sql.DB, userID, productID int64) (err error) { // Open transaction // operation views and product_viewers Two tables tx, err := db.Begin() if err != nil { return } defer func() { switch err { case nil: err = tx.Commit() default: tx.Rollback() } }() // to update products surface if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil { return } // product_viewers Insert a piece of data into the table if _, err = tx.Exec( "INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil { return } return}func main() { // Be careful : There is no need for a real connection in the process of testing db, err := sql.Open("mysql", "[email protected]/blog") if err != nil { panic(err) } defer db.Close() // userID by 1 The user browsed productID by 5 Products if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil { panic(err) }} Now we need to create a code for recordStats Function writing unit test , But I don't want to connect to the real database to test during the test . At this time, we can use it as in the following example code sqlmock Tool go mock Database operation .
package mainimport ( "fmt" "testing" "github.com/DATA-DOG/go-sqlmock")// TestShouldUpdateStats sql Execute successful test cases func TestShouldUpdateStats(t *testing.T) { // mock One *sql.DB object , There is no need to connect to the real database db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() // mock Execute assignment SQL The return result of statement mock.ExpectBegin() mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() // take mock Of DB Object passed into our function if err = recordStats(db, 2, 3); err != nil { t.Errorf("error was not expected while updating stats: %s", err) } // Ensure that the desired results are met if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) }}// TestShouldRollbackStatUpdatesOnFailure sql Execute the test case of failed rollback func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectBegin() mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT INTO product_viewers"). WithArgs(2, 3). WillReturnError(fmt.Errorf("some error")) mock.ExpectRollback() // now we execute our method if err = recordStats(db, 2, 3); err == nil { t.Errorf("was expecting an error, but there was none") } // we make sure that all expectations were met if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) }}In the above code , A successful test case and a failed rollback test case are defined , Make sure that every logical branch in our code can be tested , It improves the coverage of unit tests while ensuring the robustness of the code .
Perform unit tests , Take a look at the final test results .
* go test -v
=== RUN TestShouldUpdateStats
--- PASS: TestShouldUpdateStats (0.00s)
=== RUN TestShouldRollbackStatUpdatesOnFailure
--- PASS: TestShouldRollbackStatUpdatesOnFailure (0.00s)
PASS
ok golang-unit-test-demo/sqlmock_demo 0.011s
You can see that the results of both test cases are in line with expectations , Unit test passed .
In many applications ORM Tool scenario , You can also use go-sqlmock library mock Test the database operation .
Except that it is often used MySQL Outside ,Redis It is often used in daily development . The next section , We'll learn how to do it in unit tests mock Redis Related operations of .
miniredis It's pure. go Implemented for unit testing redis server. It's an easy to use 、 Memory based redis succedaneum , It has real TCP Interface , You can think of it as redis Version of net/http/httptest.
When we include for some Redis Operation code can be used to write unit tests mock Redis operation .
installgo get github.com/alicebob/miniredis/v2 Examples of use Here we use github.com/go-redis/redis Library as an example , Write a program that contains several Redis Operation of the DoSomethingWithRedis function .
// redis_op.gopackage miniredis_demoimport ( "context" "github.com/go-redis/redis/v8" // Note the import version "strings" "time")const ( KeyValidWebsite = "app:valid:website:list")func DoSomethingWithRedis(rdb *redis.Client, key string) bool { // This can be right redis Some logic of operation ctx := context.TODO() if !rdb.SIsMember(ctx, KeyValidWebsite, key).Val() { return false } val, err := rdb.Get(ctx, key).Result() if err != nil { return false } if !strings.HasPrefix(val, "https://") { val = "https://" + val } // Set up blog key Five seconds expired if err := rdb.Set(ctx, "blog", val, 5*time.Second).Err(); err != nil { return false } return true} The following code is what I use miniredis The library is DoSomethingWithRedis Function to write unit test code , among miniredis Not only support mock frequently-used Redis operation , It also provides many useful help functions , For example, inspection key Whether the value of is equal to the expected value s.CheckGet() And help check key Overdue s.FastForward().
// redis_op_test.gopackage miniredis_demoimport ( "github.com/alicebob/miniredis/v2" "github.com/go-redis/redis/v8" "testing" "time")func TestDoSomethingWithRedis(t *testing.T) { // mock One redis server s, err := miniredis.Run() if err != nil { panic(err) } defer s.Close() // Prepare the data s.Set("q1mi", "liwenzhou.com") s.SAdd(KeyValidWebsite, "q1mi") // Connect mock Of redis server rdb := redis.NewClient(&redis.Options{ Addr: s.Addr(), // mock redis server The address of }) // Call function ok := DoSomethingWithRedis(rdb, "q1mi") if !ok { t.Fatal() } // It can be checked manually redis Whether the value in is compound with the expected value if got, err := s.Get("blog"); err != nil || got != "https://liwenzhou.com" { t.Fatalf("'blog' has the wrong value") } // You can also use the help tool to check s.CheckGet(t, "blog", "https://liwenzhou.com") // Overdue inspection s.FastForward(5 * time.Second) // Fast forward 5 second if s.Exists("blog") { t.Fatal("'blog' should not have existed anymore") }}Execute execute test , Look at the unit test results :
* go test -v
=== RUN ;TestDoSomethingWithRedis
--- PASS: TestDoSomethingWithRedis (0.00s)
PASS
ok golang-unit-test-demo/miniredis_demo 0.052s
miniredis Basically, it supports the vast majority of Redis command , You can learn more about the usage by checking the documentation .
Except for using miniredis Build local redis server In addition to this method , Various pile driving tools can also be used to drive piles for specific methods . What kind of mock The way should be decided according to the actual situation .
How to deal with the dependency of database is the most common problem when writing unit tests for code in daily work development , This article explains how to use it go-sqlmock and miniredis Tools mock Related dependencies .
Next , We will go further , Describes in detail how to write unit tests mock Interface implementation , More about Go database CRUD Mock For testing information, please pay attention to other relevant articles on the software development network !
边栏推荐
- Installing component and package libraries in Ad
- 英文论文要怎么查重?
- How to check the duplicate of an English paper?
- Data types in JS (basic)
- When Jerry made Bluetooth transmission, when he modified stereo to mono differential output, there was a jam sound at the receiving end [chapter]
- Xmind8 最新破解教程(亲测有用)
- 杰理之SD 卡复用 iic 注意问题【篇】
- 可以在网上炒股开户吗?是安全的吗
- matplotlib plt. Details of subplots()
- Eureka控制台访问微服务暴露的info端点
猜你喜欢

Data types in JS (basic)

What is EGFP, green fluorescent protein

7.目标检测

科研漫畫 | 看圖可以學腦電,來試試?

The latest simulation test questions and answers of Henan construction electrician (special construction operation) in 2022

12.信号基础

Delaying patient self-help guide | "I have 100 excuses for not delaying, but I am not willing to take a step"

Xr34082a high efficiency boost dc/dc regulator IC

Mendeley installation, configuration and use
![Jerry's problem of playing songs after opening four channel EQ [chapter]](/img/ef/16d630b44df9b1c700bf8cf6acf8b2.png)
Jerry's problem of playing songs after opening four channel EQ [chapter]
随机推荐
Caricature scientifique | Vous pouvez apprendre l'EEG en regardant les images. Voulez - vous essayer?
cv/nlp哪些小方向好发论文?
Product innovation - an innovative social app that returns to real life
9 strategies for improving software development process
Qx2308 high efficiency PFM Synchronous Boost dc/dc converter
In the anchoring stage of retail digitalization, more attention is paid to how to mine and transform traffic by means of Digitalization
#15迭代器
测评 | 在生活中,你是一位什么样的人呢?
ctfshow 105-127
Jerry's problem of playing songs after opening four channel EQ [chapter]
Implementation principle of global load balancing
Which small directions of cv/nlp are easy to publish papers?
JS中的构造函数(重点)
用keil 5编译C51时出现定义未使用的处理方法
类似中国知网但是搜索英文文献的权威网站有哪些?
Go单元测试对数据库CRUD进行Mock测试
Go语言自学系列 | golang标准库encoding/xml
基于接口划分VLAN:静态VLAN【未完善】
M3608 boost IC chip high efficiency 2.6a boost dc/dc converter
How to search foreign literature efficiently?