英文:
Go: Run test from multiple package with DB initialization
问题
我有一个GO项目,具有以下项目结构(每个包中有多个这种文件对)。
- api
- userHandler.go
- userHandler_test.go
- database
- user.go
- user_test.go
在user.go中,我有User结构体和用于创建/获取/更新用户的函数(我正在使用GORM,但这不是问题所在)。在user_test.go中。
我希望每个不同的文件都能清除数据库(删除所有数据或处于某种状态),所以我尝试为每个文件创建1个suite(使用Testify),然后使用SetupSuite函数,但行为似乎不确定,可能我做错了什么。
所以我的问题是:
- 有哪种方法可以共享数据库连接?使用全局变量是最好的选择吗?
- 有哪种方法可以在每个file_test.go运行之前创建数据库表,然后使用自定义数据初始化数据库?
现在我还遇到一个奇怪的错误:运行
go test path/package1
go test path/package2
一切正常,但如果我运行(用于测试所有包)
cd path && go test ./...
我会遇到似乎不确定的错误,这就是为什么我猜测数据库连接没有正确处理的原因。
英文:
I have a GO project with this project structure (multiple couples of this kind of files in each package).
- api
- userHandler.go
- userHandler_test.go
- database
- user.go
- user_test.go
Inside user.go I have the User struct and the functions to Create/Get/Update a User (I'm using GORM but this is not the issue). In the user_test.go.
I'd like to have the DB cleaned (with all the data removed or in a certain state) for each different file, so I've tried to create 1 suite (using Testify) for each file, then use the SetupSuite function but the behaviour seems not deterministic, and probably I'm doing something wrong.
So my questions are:
- Which is the best way to have a DB connection shared? Using a global variable is the best option?
- Which is the best way to create the tables in the DB once and then init the DB with custom data before each file_test.go is run?
Right now I'm also having a strange bug: running
go test path/package1
go test path/package2
Everything works fine, but if I run (for testing all the packages)
cd path && go test ./...
I have errors that seems not to be deterministic, that's why I'm guessing that the DB connection is not handled properly
答案1
得分: 1
如果你的api
包依赖于你的database
包(看起来是这样的),那么你的api
包应该有一种方法来提供一个数据库连接池(比如一个*sql.DB
)给它。
在你的api
包的测试中,你应该只传入一个初始化的连接池(可能已经预先填充了测试模式/固定数据),然后你可以使用它。这可以是一个在api
包的init()
函数中初始化的全局变量,或者是每个测试函数中的setup()
和defer teardown()
模式。
下面是前者(最简单)的方法,你只需创建一个共享的数据库和模式供你的测试使用。
package database
import testing
var testDB *sql.DB
// 这个函数在你的实际测试函数之前运行。
func init() {
var err error
db, err = sql.Open(...)
if err != nil {
log.Fatalf("test init failed: %s", err)
}
_, err := db.Exec(`CREATE TABLE ....`)
if err != nil {
log.Fatalf("test schema creation failed: %s", err)
}
}
一些建议:
- 你也可以调用一个
setup()
函数来创建一个带有随机后缀的表,并插入你的测试数据,这样你的测试就不会使用相同的测试表(从而冲突或依赖于彼此)。在defer teardown()
函数中捕获该表名并删除它。 - 值得阅读一下这篇文章:https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091,可以获得一些额外的观点。
英文:
If your api
package depends on your database
package (which it appears to) then your api
package should have a way to provide a database connection pool (e.g. a *sql.DB
) to it.
In your tests for the api
package, you should just pass in an initialised pool (perhaps with the test schema/fixtures pre-populated) that you can use. This can either be a global you initialise in init()
for the api
package or a setup()
and defer teardown()
pattern in each test function.
Here's the former (simplest) approach where you just create a shared database and schema for your tests to use.
package database
import testing
var testDB *sql.DB
// This gets run before your actual test functions do.
func init() {
var err error
db, err = sql.Open(...)
if err != nil {
log.Fatalf("test init failed: %s", err)
}
_, err := db.Exec(`CREATE TABLE ....`)
if err != nil {
log.Fatalf("test schema creation failed: %s", err)
}
}
Some tips:
- You can also call a
setup()
function can create a table with a random suffix and insert your test data so that your tests don't use the same test table (and therefore risk conflicting or relying on each other). Capture that table name and dump it in yourdefer teardown()
function. - https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091 is worth reading for some additional perspective.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论