英文:
Go: how to run tests for multiple packages?
问题
我在src/目录下有多个子目录,使用go test
命令可以成功运行每个子目录下的测试。
但是当我尝试使用go test ./...
命令运行所有测试时,测试会运行但是失败了。
这些测试是针对本地数据库服务器运行的,每个测试文件都有带有数据库指针的全局变量。
我尝试使用-parallel 1
参数来防止数据库争用,但是测试仍然失败。
这里可能出了什么问题?
编辑:一些测试失败是因为缺少数据库条目,我在每个测试之前和之后都完全清空了数据库。我唯一能想到的原因是测试之间存在争用。
编辑2:
我的每个测试文件都有两个全局变量(使用mgo库):
var session *mgo.Session
var db *mgo.Database
还有以下的设置和拆卸函数:
func setUp() {
s, err := cfg.GetDBSession()
if err != nil {
panic(err)
}
session = s
db = cfg.GetDB(session)
db.DropDatabase()
}
func tearDown() {
db.DropDatabase()
session.Close()
}
每个测试都会在setUp()
函数中启动,使用defer tearDown()
进行拆卸。
还有一个名为cfg的包:
package cfg
import (
"labix.org/v2/mgo"
)
func GetDBSession() (*mgo.Session, error) {
session, err := mgo.Dial("localhost")
return session, err
}
func GetDB(session *mgo.Session) *mgo.Database {
return session.DB("test_db")
}
编辑3:
我将cfg修改为使用随机数据库,测试通过了。似乎多个包的测试在某种程度上是并行运行的。
是否有可能强制go test
按顺序运行所有包的测试?
英文:
I have multiple packages under a subdirectory under src/,
running the tests for each package with go test
is working fine.
When trying to run all tests with go test ./...
the tests are running but it fails..
the tests are running against local database servers, each test file has global variables with db pointers.
I tried to run the tests with -parallel 1
to prevent contention in the db, but the tests still fail.
what can be the issue here?
EDIT: some tests are failing on missing DB entries, I completely clear the DB before and after each test. the only reason I can think of why this is happening is because of some contention between tests.
EDIT 2:
each one of my test files has 2 global variables (using mgo):
var session *mgo.Session
var db *mgo.Database
also it has the following setup and teardown functions:
func setUp() {
s, err := cfg.GetDBSession()
if err != nil {
panic(err)
}
session = s
db = cfg.GetDB(session)
db.DropDatabase()
}
func tearDown() {
db.DropDatabase()
session.Close()
}
each tests startup with setUp()
and defer tearDown()
also cfg is:
package cfg
import (
"labix.org/v2/mgo"
)
func GetDBSession() (*mgo.Session, error) {
session, err := mgo.Dial("localhost")
return session, err
}
func GetDB(session *mgo.Session) *mgo.Database {
return session.DB("test_db")
}
EDIT 3:
I changed cfg to use a random database, the tests passed.
it seems that the tests from multiple packages are running somewhat in parallel.
is it possible to force go test
to run everything sequentially across packages ?
答案1
得分: 21
更新:正如@Gal Ben-Haim指出的那样,添加(未记录的)go test -p 1
标志可以按顺序构建和测试所有包。正如Go源代码中的testflag使用消息所述:
> -p=n:并行构建和测试最多n个包
旧答案:
当运行go test ./...
时,不同包的测试实际上是并行运行的,即使你设置了parallel=1
(只有特定包内的测试被保证一次只运行一个)。如果重要的是按顺序测试包,比如涉及数据库设置/拆卸的情况,目前似乎唯一的方法是使用shell来模拟go test ./...
的行为,并强制逐个测试包。
例如,像这样在Bash中工作:
find . -name '*.go' -printf '%h\n' | sort -u | xargs -n1 -P1 go test
该命令首先列出包含*.go
文件的所有子目录。然后使用sort -u
仅列出每个子目录一次(删除重复项)。最后,所有包含go文件的子目录通过xargs
传递给go test
。-P1
表示一次最多运行一个命令。
不幸的是,这比仅运行go test ./...
要复杂得多,但如果将其放入shell脚本或别名为更易记的函数中,这可能是可以接受的:
function gotest(){ find $1 -name '*.go' -printf '%h\n' | sort -u | xargs -n1 -P1 go test; }
现在可以通过调用以下命令在当前目录中运行所有测试:
gotest .
英文:
Update: As pointed out by @Gal Ben-Haim, adding the (undocumented) go test -p 1
flag builds and tests all packages in serial. As put by the testflag usage message in the Go source code:
> -p=n: build and test up to n packages in parallel
Old answer:
When running go test ./...
, the tests of the different packages are in fact run in parallel, even if you set parallel=1
(only tests within a specific package are guaranteed to be run one at a time). If it is important that the packages be tested in sequence, like when there is database setup/teardown involved, it seems like the only way right now is to use the shell to emulate the behavior of go test ./...
, and forcing the packages to be tested one by one.
Something like this, for example, works in Bash:
find . -name '*.go' -printf '%h\n' | sort -u | xargs -n1 -P1 go test
The command first lists all the subdirectories containing *.go
files. Then it uses sort -u
to list each subdirectory only once (removing duplicates). Finally all the subdirectories containing go files get fed to go test
via xargs
. The -P1
indicates that at most one command is to be run at a time.
Unfortunately, this is a lot uglier than just running go test ./...
, but it might be acceptable if it is put into a shell script or aliased into a function that's more memorable:
function gotest(){ find $1 -name '*.go' -printf '%h\n' | sort -u | xargs -n1 -P1 go test; }
Now all tests can be run in the current directory by calling:
gotest .
答案2
得分: 13
显然,运行go test -p 1
会按顺序运行所有内容(包括构建)。我在go help test
或go help testflag
中没有看到这个参数。
英文:
apparently running go test -p 1
runs everything sequentially (including build), I haven't see this argument in go help test
or go help testflag
答案3
得分: 0
我假设因为这些包分别通过了测试,所以在这种情况下,在进行这个测试之前,你也会删除数据库。
因此,听起来每个包的测试状态都应该是空的。
所以在每组包测试之间,必须清空数据库。根据你的整体情况,我将简要解释两种解决方法:
选项1. 测试设置
在每个包的测试文件开头添加一个init()
函数,然后在其中添加删除数据库的处理。这将在实际包的init()
方法之前运行:
func init() {
fmt.Println("INIT TEST")
// 我的测试状态初始化
// 删除数据库内容
}
假设包也有类似的打印行,你会在输出中看到(请注意,stdout输出仅在测试失败或您提供-v
选项时显示):
INIT TEST
INIT PACKAGE
选项2. 模拟数据库
为数据库创建一个模拟(除非这正是你要测试的内容)。模拟数据库可以始终像每个测试的起始状态一样,将数据库视为空白状态。
英文:
I am assuming that because the packages individually pass that in this situation you are also dropping the DB before that test as well.
Therefore it sounds like the state of the DB for each package test is expected to be empty.
So between each set of the package tests the DB must be emptied. There are two ways around this, not knowing your entire situation I will briefly explain both options:
Option 1. Test Setup
Add an init()
function to the start of each package _test file which you then put processing to remove the DB. This will be run before the init()
method of the actual package:
func init() {
fmt.Println("INIT TEST")
// My test state initialization
// Remove database contents
}
Assuming that the package also had a similar print line you would see in the output (note the stdout output is only displayed when the a test fails or you supply the -v
option)
INIT TEST
INIT PACKAGE
Option 2. Mock the database
Create a mock for the database (unless that is specifically what you are testing). The mock db can always act like the DB is blank for the starting state of each test.
答案4
得分: 0
我使用了一种廉价的方法来在多个包中进行测试。
go test -v ./... -run "TestUnit?"
只需给你的测试命名,这样它们就会被捕捉到:
func TestUnitCountriesStructs(t *testing.T)
你提到需要为测试设置和拆卸一些东西。我使用了testify。简而言之:
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// 在每个测试之前
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
func TestUnitExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
英文:
I used a cheap way to pick-up tests in multiple packages.
go test -v ./... -run "TestUnit?"
Just name your tests so they get caught:
func TestUnitCountriesStructs(t *testing.T)
You mentioned about needing some sort of set up and teardown for your tests. I use testify. In short:
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
suite.Equal(5, suite.VariableThatShouldStartAtFive)
}
func TestUnitExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
答案5
得分: -1
请尝试使用以下 GitHub 仓库:
https://github.com/appleboy/golang-testing
将 coverage.sh
复制到 /usr/local/bin/coverage
并更改权限。
$ curl -fsSL https://raw.githubusercontent.com/appleboy/golang-testing/master/coverage.sh /usr/local/bin/coverage
$ chmod +x /usr/local/bin/coverage
英文:
Please try out the following github repository.
https://github.com/appleboy/golang-testing
Copy coverage.sh
to /usr/local/bin/coverage
and change permission.
$ curl -fsSL https://raw.githubusercontent.com/appleboy/golang-testing/master/coverage.sh /usr/local/bin/coverage
$ chmod +x /usr/local/bin/coverage
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论