GoLang中是否存在分离单元测试和集成测试的最佳实践(作证)?我有混合的单元测试(不依赖于任何外部资源,因此运行速度非常快)和集成测试(它依赖于任何外部资源,因此运行速度较慢)。所以,我希望能够在我说go test
时控制是否包含集成测试。
最直接的技术似乎是在main中定义-integrate标志:
var runIntegrationTests = flag.Bool("integration", false
, "Run the integration tests (in addition to the unit tests)")
然后在每个集成测试的顶部添加一个if语句:
if !*runIntegrationTests {
this.T().Skip("To run this test, use: go test -integration")
}
这是我能做的最好的吗?我搜索了testify文档,看看是否有一个命名约定或某些东西可以为我完成这个,但是没有找到任何东西。我错过了什么吗?
答案 0 :(得分:110)
@ Ainar-G提出了几种分离测试的好方法。
This set of Go practices from SoundCloud建议使用构建代码(described in the "Build Constraints" section of the build package)来选择要运行的测试:
编写integration_test.go,并为其提供集成的构建标记。为服务地址和连接字符串定义(全局)标志,并在测试中使用它们。
// +build integration var fooAddr = flag.String(...) func TestToo(t *testing.T) { f, err := foo.Connect(*fooAddr) // ... }
go test会像go build一样使用构建标记,因此您可以调用
go test -tags=integration
。它还合成了一个调用flag.Parse的包main,因此任何声明和可见的标志都将被处理并可用于您的测试。
作为类似选项,您还可以使用构建条件// +build !unit
默认运行集成测试,然后通过运行go test -tags=unit
按需禁用它们。
@adamc评论:
对于其他任何尝试使用构建代码的人来说,// +build test
评论是您文件中的第一行,并且在评论后包含空行,这一点非常重要,否则{{1命令将忽略该指令。
此外,构建注释中使用的标记不能有破折号,但允许使用下划线。例如,-tags
将不起作用,而// +build unit-tests
将起作用。
答案 1 :(得分:40)
我看到三种可能的解决方案。第一种是使用short mode进行单元测试。因此,您可以使用go test -short
进行单元测试,但不使用-short
标志来运行集成测试。标准库使用短模式来跳过长时间运行的测试,或者通过提供更简单的数据使它们运行得更快。
第二种方法是使用约定并调用TestUnitFoo
或TestIntegrationFoo
测试,然后使用-run
testing flag表示要运行的测试。因此,您可以使用go test -run 'Unit'
进行单元测试,使用go test -run 'Integration'
进行集成测试。
第三个选项是使用环境变量,并使用os.Getenv
在测试设置中获取它。然后,您将使用简单的go test
进行单元测试,使用FOO_TEST_INTEGRATION=true go test
进行集成测试。
我个人更喜欢-short
解决方案,因为它更简单并且在标准库中使用,所以它似乎是分离/简化长时间运行测试的事实上的方法。但-run
和os.Getenv
解决方案提供了更大的灵活性(因为正则表达式涉及-run
),因此需要更加谨慎。
答案 2 :(得分:29)
详细说明我对@ Ainar-G的优秀答案的评论,在过去一年中,我一直在使用-short
和Integration
命名惯例的组合来实现两全其美。
构建标志以前强迫我拥有多个文件(services_test.go
,services_integration_test.go
等)。
相反,请参考下面的示例,其中前两个是单元测试,最后我进行了集成测试:
package services
import "testing"
func TestServiceFunc(t *testing.T) {
t.Parallel()
...
}
func TestInvalidServiceFunc3(t *testing.T) {
t.Parallel()
...
}
func TestPostgresVersionIntegration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
...
}
请注意,上一次测试具有以下惯例:
Integration
。-short
flag指令下运行。基本上,规范是:“正常编写所有测试。如果是长时间运行的测试或集成测试,请遵循此命名约定并检查-short
对您的同行是否好。”< / p>
go test -v -short
这为您提供了一组很好的消息,如:
=== RUN TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
service_test.go:138: skipping integration test
go test -run Integration
这只运行集成测试。适用于生产中的烟雾测试金丝雀。
显然,这种方法的缺点是,如果有人运行go test
,没有-short
标志,它将默认运行所有测试 - 单元和集成测试。
实际上,如果您的项目足够大以进行单元和集成测试,那么您很可能使用Makefile
,其中您可以使用简单的指令在其中使用go test -short
。或者,只需将其放入README.md
文件中并在当天调用即可。
答案 3 :(得分:2)
最近我正试图找到一种解决方案。 这些是我的标准:
上述解决方案(自定义标志,自定义构建标签,环境变量)并不能真正满足上述所有条件,因此,经过一番挖掘和探索之后,我想到了以下解决方案:
package main
import (
"flag"
"regexp"
"testing"
)
func TestIntegration(t *testing.T) {
if m := flag.Lookup("test.run").Value.String(); m == "" || !regexp.MustCompile(m).MatchString(t.Name()) {
t.Skip("skipping as execution was not requested explicitly using go test -run")
}
t.Parallel()
t.Run("HelloWorld", testHelloWorld)
t.Run("SayHello", testSayHello)
}
实现简单明了。尽管它需要一个简单的测试约定,但是它不太容易出错。进一步的改进可能是将代码导出到辅助函数。
仅对项目中的所有程序包运行集成测试:
go test -v ./... -run ^TestIntegration$
运行所有测试(常规和集成):
go test -v ./... -run .\*
仅运行常规测试:
go test -v ./...
此解决方案无需工具即可很好地工作,但是Makefile或某些别名可以使用户更容易使用。它也可以轻松集成到任何支持运行go测试的IDE中。
完整的示例可以在这里找到:https://github.com/sagikazarmark/modern-go-application
答案 4 :(得分:1)
我鼓励你看看 Peter Bourgons 的方法,它很简单,避免了其他答案中的建议的一些问题:https://peter.bourgon.org/blog/2021/04/02/dont-use-build-tags-for-integration-tests.html