你可以在你的Golang测试案例中使用这个简单的数据库测试夹具例子。你所需要的只是一个数据库实例和表截断功能,我为其创建了一个小包。
数据库辅助包
package test
import (
"database/sql"
"fmt"
)
func OpenDB(driver, address string, maxIdleConns int) *sql.DB {
db, _ := sql.Open(driver, address)
db.SetMaxIdleConns(maxIdleConns)
return db
}
func CloseDB(db *sql.DB) {
_ = db.Close()
}
func TruncateTables(db *sql.DB, tables []string) {
_, _ = db.Exec("SET FOREIGN_KEY_CHECKS=0;")
for _, v := range tables {
_, _ = db.Exec(fmt.Sprintf("TRUNCATE TABLE %s;", v))
}
_, _ = db.Exec("SET FOREIGN_KEY_CHECKS=1;")
}
测试
假设我们在数据库中有一个叫做leagues 的表。我们想首先截断它,使它变得干净。然后,我们想用某些记录来填充它,以便我们的测试可以使用它。基本流程如下:
-
获得一个数据库实例。
-
Truncate table
leagues. -
用测试数据填充
leagues。 -
在每个测试前明确地调用
loadFixtures函数。
帮助文件
这是为前三个步骤准备的:
package league
import (
"database/sql"
"fmt"
"os"
"strings"
"testing"
"internal/pkg/test"
)
// -----------------------------------------------------------------------------
var DB *sql.DB
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}
func setup() {
// Prepare database.
dbAdr := "user:pass@tcp(0.0.0.0:3306)/football?charset=utf8mb4&collation=utf8mb4_unicode_ci"
dbDrv := "mysql"
dbIdl := 5
// Obtain a database instance.
DB = test.OpenDB(dbDrv, dbAdr, dbIdl)
fmt.Printf("\033[1;36m%s\033[0m", "> Setup completed\n")
}
func teardown() {
// Close the database instance.
test.CloseDB(DB)
fmt.Printf("\033[1;36m%s\033[0m", "> Teardown completed")
fmt.Printf("\n")
}
// -----------------------------------------------------------------------------
func loadFixtures(db *sql.DB) {
test.TruncateTables(db, []string{"leagues"})
fmt.Printf("\033[1;36m%s\033[0m", "> Tables truncated\n")
data := []struct {
name interface{}
isActive interface{}
createdAt interface{}
deletedAt interface{}
}{
{
"La Liga",
"true",
"2019-12-31 23:59:59",
"null",
},
{
"Premier League",
"true",
"2019-12-31 23:59:59",
"null",
},
}
query := `
INSERT INTO leagues
(name, is_active, created_at, deleted_at)
VALUES
`
for _, d := range data {
query += fmt.Sprintf(
"('%v', %v, '%v', %v),\n",
d.name,
d.isActive,
d.createdAt,
d.deletedAt,
)
}
query = strings.TrimSuffix(query, ",\n")
_, _ = db.Exec(query)
fmt.Printf("\033[1;36m%s\033[0m", "> Fixtures loaded\n")
}
重要说明
根据你的测试,你极有可能得到一个类似于Error 1062: Duplicate entry '1' for key 'PRIMARY' 的错误。无论这种情况是否发生,你必须在test.TruncateTables() 行之前执行LOCK TABLES {your_table_name} WRITE; ,然后在UNLOCK TABLES; 。
测试文件
这是在最后一步,你的测试调用loadFixtures 函数:
package league
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestCreate(t *testing.T) {
// DB is already ready for you!
loadFixtures(DB)
rq := httptest.NewRequest(http.MethodGet, "/leagues", nil)
rw := httptest.NewRecorder()
createHandler := NewCreate(DB)
createHandler.Create(rw, rq)
// Assert against rw.Body.String()
}
输出
正如你在下面看到的,我只有TestCreate 测试案例依赖于固定装置,而不是其他的:
$ go test ./... -v
> Setup completed
=== RUN TestRetrieve
--- PASS: TestRetrieve (0.00s)
> Tables truncated
> Fixtures loaded
=== RUN TestCreate
--- PASS: TestCreate (0.00s)
=== RUN TestList
--- PASS: TestList (0.00s)
PASS
> Teardown completed
ok internal/league 0.051s