Sqlmock
sqlmock 是一个实现 数据库测试的Mock库,它不需要建立真正的数据库就可以测试CRUD一些操作,很方便
go get github.com/DATA-DOG/go-sqlmock
案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package mainimport "database/sql" func recordStats (db *sql.DB, userID, productID int64 ) (err error ) { tx, err := db.Begin() if err != nil { return } defer func () { switch err { case nil : err = tx.Commit() default : tx.Rollback() } }() if _, err = tx.Exec("UPDATE products SET views = views + 1" ); err != nil { return } if _, err = tx.Exec( "INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)" , userID, productID); err != nil { return } return } func main () { db, err := sql.Open("mysql" , "root@/blog" ) if err != nil { panic (err) } defer db.Close() if err = recordStats(db, 1 , 5 ); err != nil { panic (err) } }
编写测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package mainimport ( "fmt" "testing" "github.com/DATA-DOG/go-sqlmock" ) func TestShouldUpdateStats (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 ).WillReturnResult(sqlmock.NewResult(1 , 1 )) mock.ExpectCommit() if err = recordStats(db, 2 , 3 ); err != nil { t.Errorf("error was not expected while updating stats: %s" , err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s" , err) } } 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() if err = recordStats(db, 2 , 3 ); err == nil { t.Errorf("was expecting an error, but there was none" ) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s" , err) } }
运行后 1 2 3 4 5 6 ❯ go test -v === RUN TestShouldUpdateStats --- PASS: TestShouldUpdateStats (0.00s) === RUN TestShouldRollbackStatUpdatesOnFailure --- PASS: TestShouldRollbackStatUpdatesOnFailure (0.00s) PASS
总结: 其实就是官方案例,有兴趣去看官方案例
miniRedis
miniRedis 是一个纯go实现的用于单元测试的redis server。它是一个简单易用的、基于内存的redis替代品,它具有真正的TCP接口.
import “github.com/alicebob/miniredis/v2”
被测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package sqlTimport ( "context" "github.com/redis/go-redis/v9" "strings" "time" ) const ( KeyValidWebsite = "app:valid:website:list" ) func DoSomethingWithRedis (rdb *redis.Client, key string ) bool { 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 } if err := rdb.Set(ctx, "blog" , val, 5 *time.Second).Err(); err != nil { return false } return true }
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package sqlTimport ( "github.com/redis/go-redis/v9" "testing" "time" ) import "github.com/alicebob/miniredis/v2" func TestDoSomethingWithRedis (t *testing.T) { s := miniredis.RunT(t) s.Set("key" , "value" ) s.SAdd(KeyValidWebsite, "key" ) rdb := redis.NewClient(&redis.Options{ Addr: s.Addr(), }) if !DoSomethingWithRedis(rdb, "key" ) { t.Error("DoSomethingWithRedis failed" ) } if got, err := s.Get("blog" ); err != nil || got != "https://value" { t.Error("DoSomethingWithRedis failed" ) } s.FastForward(5 * time.Second) if s.Exists("blog" ) { t.Error("DoSomethingWithRedis failed" ) } }
output:
1 2 3 4 5 === RUN TestDoSomethingWithRedis --- PASS: TestDoSomethingWithRedis (0.01 s) PASS Process finished with the exit code 0
总结
多看文档