如何按顺序运行 Golang 测试?

huangapple go评论92阅读模式
英文:

How to run golang tests sequentially?

问题

当我运行go test时,我的输出如下:

--- FAIL: TestGETSearchSuccess (0.00s)
        位置:drivers_api_test.go:283
        错误:不相等:200(期望)
                                != 204(实际)
                
--- FAIL: TestGETCOSearchSuccess (0.00s)
        位置:drivers_api_test.go:391
        错误:不相等:200(期望)
                                != 204(实际)

但是当我再次运行go test时,所有的测试都通过了。

只有当我重置我的mysql数据库,然后第一次运行go test时,测试才会失败。

对于每个GET请求,在执行之前我都会进行一个POST请求,以确保在数据库中创建了数据。

有人可以帮我解决如何确保测试按顺序运行吗?也就是说,在执行GET请求之前先执行POST请求?

英文:

When I run go test, my output:

--- FAIL: TestGETSearchSuccess (0.00s)
        Location:       drivers_api_test.go:283
        Error:          Not equal: 200 (expected)
                                != 204 (actual)
                
--- FAIL: TestGETCOSearchSuccess (0.00s)
        Location:       drivers_api_test.go:391
        Error:          Not equal: 200 (expected)
                                != 204 (actual)

But after I run go test again, all my tests pass.

Tests fail only when I reset my mysql database, and then run go test for the first time.

For every GET request, I do a POST request before to ensure that there is data created in the DB.

Could anyone help me with how to make sure that tests are run sequentially? That is the POST requests are run before the GET requests?

答案1

得分: 44

你不能/不应该依赖测试的执行顺序。测试的执行顺序是不确定的,并且通过使用测试标志,可以排除某些测试的运行,所以不能保证它们会全部运行。

例如,以下命令只会运行名称中包含字母W的测试:

go test -run W

还要注意,如果某些测试函数使用T.Parallel()方法标记自己可以并行执行,go工具将重新排序测试,首先运行非并行测试,然后在某些情况下(由测试标志如-p控制)并行运行并行测试。你可以在这个答案中看到这方面的例子:https://stackoverflow.com/questions/44325232/are-tests-executed-in-parallel-in-go-or-one-by-one/44326377#44326377

测试应该是相互独立的。如果一个测试函数有前提条件,那么这个条件不能在另一个测试函数中实现。

在运行测试函数之前执行其他任务的选项:

  • 你可以将其放在测试函数本身中。
  • 你可以将其放在包的init()函数中,在_test.go文件中。这将在执行测试函数之前运行一次。
  • 你可以选择实现一个TestMain()函数,它将首先被调用,在调用M.Run()触发测试函数的执行之前,你可以在其中进行额外的设置。
  • 你可以混合使用上述选项。

在你的情况下,在包的init()TestMain()函数中,你应该检查你的数据库是否已初始化(是否插入了测试记录),如果没有,则插入测试记录。

请注意,从Go 1.7开始,你可以使用子测试来定义子测试的执行顺序。详细信息请参阅博文:使用子测试和子基准测试,以及testing包的文档。

英文:

You can't / shouldn't rely on test execution order. The order in which tests are executed is not defined, and with the use of testing flags it is possible to exclude tests from running, so you have no guarantee that they will run at all.

For example the following command will only run tests whose name contains a 'W' letter:

go test -run W

Also note that if some test functions mark themselves eligible for parallel execution using the T.Parallel() method, the go tool will reorder the tests to first run non-parallel tests, and then run parallel tests in parallel under certain circumstances (controlled by test flags like -p). You can see examples of this in this answer: https://stackoverflow.com/questions/44325232/are-tests-executed-in-parallel-in-go-or-one-by-one/44326377#44326377

Tests should be independent from each other. If a test function has prerequisites, that cannot be done/implemented in another test function.

Options to do additional tasks before a test function is run:

  • You may put it in the test function itself
  • You may put it in a package init() function, in the _test.go file itself. This will run once before execution of test functions begins.
  • You may choose to implement a TestMain() function which will be called first and in which you may do additional setup before you call M.Run() to trigger the execution of test functions.
  • You may mix the above options.

In your case in package init() or TestMain() you should check if your DB is initialized (there are test records inserted), and if not, insert the test records.

Note that starting with Go 1.7, you may use subtests in which you define execution order of subtests. For details see blog post: Using Subtests and Sub-benchmarks, and the package doc of the testing package.

答案2

得分: 43

对于像我一样因为同时运行多个并发测试而遇到问题的人,我找到了一种限制最大并行运行测试数量的方法:

go test -p 1

使用这个命令,你的测试将按顺序一个接一个地运行。

来源

英文:

For those who as I am is getting problems because of multiple concurring tests running simultaneously. I found a way to limit the maximum number of test running in parallel:

go test -p 1

With this, your test will run sequentially one by one.

Source

答案3

得分: 24

除了像ConveyGinkgo这样的第三方库之外,使用纯粹的Golang 1.7,你可以按顺序运行测试。你可以在这里阅读更多信息。

func TestFoo(t *testing.T) {
    // <setup code>
    t.Run("A=1", func(t *testing.T) { ... })
    t.Run("A=2", func(t *testing.T) { ... })
    t.Run("B=1", func(t *testing.T) { ... })
    // <tear-down code>
}

你可以使用以下方式有条件地运行它们:

go test -run ''      # 运行所有测试。
go test -run Foo     # 运行与"Foo"匹配的顶级测试,例如"TestFooBar"。
go test -run Foo/A=  # 对于与"Foo"匹配的顶级测试,运行与"A="匹配的子测试。
go test -run /A=1    # 对于所有顶级测试,运行与"A=1"匹配的子测试。

假设你从一个REST API中获得了一个user包,你想要测试它。你需要测试创建处理程序,以便能够测试登录处理程序。通常,我会将以下代码放在user_test.go文件中:

type UserTests struct { Test *testing.T}

func TestRunner(t *testing.T) {
    t.Run("A=create", func(t *testing.T) {
        test := UserTests{Test: t}
        test.TestCreateRegularUser()
        test.TestCreateConfirmedUser()
        test.TestCreateMasterUser()
        test.TestCreateUserTwice()
    })
    t.Run("A=login", func(t *testing.T) {
        test := UserTests{Test: t}
        test.TestLoginRegularUser()
        test.TestLoginConfirmedUser()
        test.TestLoginMasterUser()
    })
}

然后,我可以在任何_test.go文件中为UserTest类型附加方法,这些方法不会被go test命令执行:

func (t *UserTests) TestCreateRegularUser() {
    registerRegularUser := util.TableTest{
        Method:      "POST",
        Path:        "/iot/users",
        Status:      http.StatusOK,
        Name:        "registerRegularUser",
        Description: "register Regular User has to return 200",
        Body:        SerializeUser(RegularUser),
    }
    response := util.SpinSingleTableTests(t.Test, registerRegularUser)
    util.LogIfVerbose(color.BgCyan, "IOT/USERS/TEST", response)
}
英文:

Apart for 3rd party libraries like Convey and Ginkgo, with plain Golang 1.7 you can run tests sequentially. You can read more here

func TestFoo(t *testing.T) {
    // &lt;setup code&gt;
    t.Run(&quot;A=1&quot;, func(t *testing.T) { ... })
    t.Run(&quot;A=2&quot;, func(t *testing.T) { ... })
    t.Run(&quot;B=1&quot;, func(t *testing.T) { ... })
    // &lt;tear-down code&gt;
}

And you can run them conditionally with:

go test -run &#39;&#39;      # Run all tests.
go test -run Foo     # Run top-level tests matching &quot;Foo&quot;, such as &quot;TestFooBar&quot;.
go test -run Foo/A=  # For top-level tests matching &quot;Foo&quot;, run subtests matching &quot;A=&quot;.
go test -run /A=1    # For all top-level tests, run subtests matching &quot;A=1&quot;.

So lets say you got an user package from a REST api that you want to test. You need to test the create handler in order to be able to test the login handler. Usually I would have this on the user_test.go

type UserTests struct { Test *testing.T}
func TestRunner(t *testing.T) {

	t.Run(&quot;A=create&quot;, func(t *testing.T) {
		test:= UserTests{Test: t}
		test.TestCreateRegularUser()
		test.TestCreateConfirmedUser()
		test.TestCreateMasterUser()
		test.TestCreateUserTwice()
	})
	t.Run(&quot;A=login&quot;, func(t *testing.T) {
		test:= UserTests{Test: t}
		test.TestLoginRegularUser()
		test.TestLoginConfirmedUser()
		test.TestLoginMasterUser()
	})

}

Then I can append methods to the UserTest type that wont be executed by the go test command in any _test.go file

func (t *UserTests) TestCreateRegularUser() {
	registerRegularUser := util.TableTest{
		Method:      &quot;POST&quot;,
		Path:        &quot;/iot/users&quot;,
		Status:      http.StatusOK,
		Name:        &quot;registerRegularUser&quot;,
		Description: &quot;register Regular User has to return 200&quot;,
		Body: SerializeUser(RegularUser),
	}
	response := util.SpinSingleTableTests(t.Test, registerRegularUser)
	util.LogIfVerbose(color.BgCyan, &quot;IOT/USERS/TEST&quot;, response)
}

答案4

得分: 4

要实现这一点的最佳方法是创建一个TestMain,如此处所示:链接

import (
  "testing"
  "os"
)

func TestMain(m *testing.M) {
   // 在这里执行你的操作
   os.Exit(m.Run())
}
英文:

The best way to achieve that is to create a TestMain, as presented here.

import (
  &quot;testing&quot;
  &quot;os&quot;
)

func TestMain(m *testing.M) {
   // Do your stuff here
   os.Exit(m.Run())
}

答案5

得分: 2

也可以使用等待组(wait groups)来同步测试:

awaitRootElement := sync.WaitGroup{}
awaitRootElement.Add(1)

t.Run("it should create root element", func(t0 *testing.T) {
    // 进行创建根元素的测试
    awaitRootElement.Done()
})

awaitRootElement.Wait()

t.Run("it should act on root element somehow", func(t0 *testing.T) {
    // 进行依赖于根元素的测试
})

请注意,在安排测试之前,应该先等待,因为异步测试执行可能会导致死锁(测试例程正在等待另一个永远不会运行的测试)。

英文:

It's also possible to synchronize the test using wait groups:

	awaitRootElement := sync.WaitGroup{}
	awaitRootElement.Add(1)

	t.Run(&quot;it should create root element&quot;, func(t0 *testing.T) {
		// do the test which creates root element
		awaitRootElement.Done()
	})
    awaitRootElement.Wait()
    t.Run(&quot;it should act on root element somehow&quot;, func(t0 *testing.T) {
      // do tests which depend on root element
    })

Note that you should wait before scheduling the tests, since the asynchronous test execution otherwise might deadlock (the test routine is awaiting another test which never gets to run).

huangapple
  • 本文由 发表于 2015年7月3日 16:14:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/31201858.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定