在Go中分离单元测试和集成测试

Cra*_*nes 82 integration-testing unit-testing go testify

GoLang中是否存在分离单元测试和集成测试的最佳实践(作证)?我有混合的单元测试(不依赖于任何外部资源,因此运行速度非常快)和集成测试(它依赖于任何外部资源,因此运行速度较慢).因此,我希望能够控制是否包含集成测试go test.

最直接的技术似乎是在main中定义-integrate标志:

var runIntegrationTests = flag.Bool("integration", false
    , "Run the integration tests (in addition to the unit tests)")
Run Code Online (Sandbox Code Playgroud)

然后在每个集成测试的顶部添加一个if语句:

if !*runIntegrationTests {
    this.T().Skip("To run this test, use: go test -integration")
}
Run Code Online (Sandbox Code Playgroud)

这是我能做的最好的吗?我搜索了testify文档,看看是否有一个命名约定或者某些内容为我完成了这个,但没有找到任何东西.我错过了什么吗?

Ale*_*lex 131

@ Ainar-G提出了几个很好的模式来分开测试.

SoundCloud的这组Go实践建议使用构建标记(在构建包的"构建约束"部分中描述)来选择要运行的测试:

编写integration_test.go,并为其提供集成的构建标记.为服务地址和连接字符串定义(全局)标志,并在测试中使用它们.

// +build integration

var fooAddr = flag.String(...)

func TestToo(t *testing.T) {
    f, err := foo.Connect(*fooAddr)
    // ...
}
Run Code Online (Sandbox Code Playgroud)

go test需要像build build一样构建标签,所以你可以调用go test -tags=integration.它还合成了一个调用flag.Parse的包main,因此任何声明和可见的标志都将被处理并可用于您的测试.

作为类似选项,您还可以使用构建条件默认运行集成测试// +build !unit,然后通过运行按需禁用它们go test -tags=unit.

@adamc评论:

对于其他试图使用构建标记的人来说,重要的是// +build test注释是文件中的第一行,并且在注释后包含一个空行,否则-tags命令将忽略该指令.

此外,构建注释中使用的标记不能有破折号,但允许使用下划线.例如,// +build unit-tests将不会工作,而// +build unit_tests将.

  • @ Tyler.z.yang你能提供关于标签弃用的链接或更多细节吗?我没有找到这样的信息.我正在使用带有go1.8的标签,用于答案中描述的方式,以及用于模拟测试中的类型和函数.它是我认为的接口的好选择. (2认同)
  • 对于其他试图使用构建标记的人来说,重要的是`// + build`测试注释是文件中的第一行,并且在注释后包含一个空行,否则`-tags`命令将忽略指示.此外,构建注释中使用的标记不能有破折号,但允许使用下划线.例如,`// + build unit-tests`将不起作用,而`// + build unit_tests`将起作用 (2认同)
  • 如何处理通配符?`go test -tags = integration。/ ...`不起作用,它忽略了标签 (2认同)

Ain*_*r-G 45

我看到三种可能的解决方案 第一种是使用短模式进行单元测试.因此,您将使用go test -short单元测试和相同但没有-short标志来运行集成测试.标准库使用短模式来跳过长时间运行的测试,或者通过提供更简单的数据使它们运行得更快.

第二是使用公约,并打电话给你测试,无论是TestUnitFooTestIntegrationFoo再使用-run测试标志来表示要运行的测试.因此,您将go test -run 'Unit'用于单元测试和go test -run 'Integration'集成测试.

第三个选项是使用环境变量,并在测试设置中使用os.Getenv.然后,您将使用简单go test的单元测试和FOO_TEST_INTEGRATION=true go test集成测试.

我个人更喜欢这个-short解决方案,因为它更简单并且在标准库中使用,所以它似乎是分离/简化长时间运行测试的事实上的方法.但是-runos.Getenv解决方案提供了更大的灵活性(因为涉及正则表达式,所以也需要更加谨慎-run).

  • 请注意,IDE(Atom、Sublime 等)常见的社区测试运行器(例如,“Tester-Go”)具有使用“-short”标志以及“-coverage”等运行的内置选项。因此,我在测试名称中结合使用了 Integration 以及在这些测试中进行的 `if testing.Short()` 检查。它让我能够两全其美:在 IDE 中使用 `-short` 运行,并使用 `go test -run "Integration"` 显式地仅运行集成测试 (2认同)

edu*_*911 38

要在我的评论阐述为@ Ainar-G的出色答卷,在过去的一年我一直在使用的组合-shortIntegration命名约定以达到两全其美.

单元和集成测试和谐,在同一个文件中

建立标志先前迫使我有多个文件(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")
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个测试具有以下约定:

  1. 使用Integration的测试名称.
  2. 检查是否在-shortflag指令下运行.

基本上,规范是:"正常编写所有测试.如果是长时间运行的测试,或者是集成测试,请遵循此命名约定并检查对-short同行的好处."

仅运行单元测试:

go test -v -short
Run Code Online (Sandbox Code Playgroud)

这为您提供了一组很好的消息,例如:

=== RUN   TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
        service_test.go:138: skipping integration test
Run Code Online (Sandbox Code Playgroud)

仅运行集成测试:

go test -run Integration
Run Code Online (Sandbox Code Playgroud)

这只运行集成测试.适用于生产中的烟雾测试金丝雀.

显然,这种方法的缺点是,如果有人运行go test,没有-short标志,它将默认运行所有测试 - 单元和集成测试.

实际上,如果您的项目足够大以进行单元和集成测试,那么您很可能正在使用可以Makefile在其中使用简单指令的位置go test -short.或者,只需将其放入您的README.md文件中并在当天调用即可.

  • 喜欢简单 (3认同)
  • 好一个。为了仅运行集成测试,我必须使用 go regex ```go test -v -run ".Integration" ./...``` 这里 [go regex](https://golang.org/pkg/regexp /syntax/) 和一个[很好的例子](https://zetcode.com/golang/regex/) (2认同)

mar*_*zar 9

我最近试图找到相同的解决方案。这些是我的标准:

  • 解决方案必须是通用的
  • 没有单独的集成测试包
  • 分离应该是完整的(我应该能够运行集成测试
  • 集成测试没有特殊的命名约定
  • 它应该在没有额外工具的情况下运行良好

前面提到的解决方案(自定义标志、自定义构建标记、环境变量)并没有真正满足上述所有条件,所以经过一番挖掘和尝试,我想出了这个解决方案:

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)
}
Run Code Online (Sandbox Code Playgroud)

实现是简单和最小的。虽然它需要一个简单的测试约定,但它不太容易出错。进一步的改进可能是将代码导出到辅助函数。

用法

仅对项目中的所有包运行集成测试:

go test -v ./... -run ^TestIntegration$
Run Code Online (Sandbox Code Playgroud)

运行所有测试(常规和集成):

go test -v ./... -run .\*
Run Code Online (Sandbox Code Playgroud)

仅运行常规测试:

go test -v ./...
Run Code Online (Sandbox Code Playgroud)

此解决方案无需工具即可正常运行,但 Makefile 或某些别名可以使其更易于用户使用。它还可以轻松集成到任何支持运行 go 测试的 IDE 中。

完整示例可以在这里找到:https : //github.com/sagikazarmark/modern-go-application


dan*_*mux 6

我鼓励您看看 Peter Bourgons 的方法,它很简单,并且避免了其他答案中建议的一些问题:https ://peter.bourgon.org/blog/2021/04/02/dont-use-build-tags -for-integration-tests.html