有些测试可能在第二次运行时才会失败,但GoLand的集成调试默认只运行一次。
我们怎样才能用它来调试它们呢?
相互依赖的测试的问题
在理想的代码中,单元测试之间有很好的隔离,它们的依赖关系在每次迭代中都被正确初始化。
但在现实世界中,情况并不总是这样,一个测试在第一次运行时可以完美地成功,没有提醒潜在的问题,直到一个更好奇的测试人员以不同的顺序运行测试套件(用go test -shuffle )或几次(用go test -count=2 ),让他们发现了问题所在。
发生这种情况的一个原因可能是导入的包使用了一个全局变量,其值会从一个测试持续到另一个测试,导致不同的行为取决于测试的顺序,或者序列被执行第二次的事实。
这就造成了这样的情况:一个正常的执行会成功,但对于同一个序列,另一个会失败:
go test→ 命中go test -count=2→ 第一遍成功,第二遍失败go test -shuffle=1→ 有时成功,有时失败
这时,我们如何用GoLand中内置的Delve调试器来调试这种情况?
调试的默认 "运行配置"
要在GoLand中调试一个测试,只需在编辑器中点击位于感兴趣的测试名称旁边的绿色三角形,并选择如下图所示的调试选项:

这将创建一个默认的运行配置,并开始以调试模式运行。
问题是,这种执行只发生一次,不会触发问题。
第一次尝试:模仿go test -count=2
我们如何在命令行中解决这个问题呢?在上面的例子中,我们会使用例如这样的语法:
go test -count=2 -run=TestCountNumbers .
看一下GoLand刚刚为我们的第一次尝试创建的运行配置,我们确实发现了一个地方可以添加参数到go test:

然而,当运行时,测试仍然只执行一次:

这到底是怎么回事?通过展开*<4 go setup calls>*来解释,看看GoLand在这个设置中是如何实际运行测试的。

在这些配置中,如前面的截图所示,GoLand运行测试的方式与我们在CLI上运行测试的方式有些不同:
- 它首先用这些选项建立一个测试可执行文件。
-c产生一个测试二进制文件,而不是即时运行它-o <path>把它放在一个私人位置
- 然后,它在Delve启动器上运行
go tool test2json,传递第一步建立的程序,以及一些由(自我生成的或手动的)TestMain拦截的标志。-test.v显示所有测试-test.paniconexit0如果一个测试执行了,就触发panicos.Exit(0)-test.run ^\QTestCountNumbers\E$只运行选定的测试- ...但没有别的:
-count=2参数不是操作的一部分。
当运行该配置而不是使用调试时,机制是相同的,除了GoLand从go tool test2json 自己运行测试二进制,而不是让Delve运行它。
解决方案
让我们考虑一下GoLand是如何转换测试参数的:
- 它产生测试二进制文件,可能使用可能适用于这一步骤的
go test的参数,其中-count不是其中之一 - 运行
go tool test2json,使用--分隔符,将用于测试二进制的标志和参数与用于go tool本身的标志和参数分开。
如果我们重新检查测试配置,在为go test 的Go工具参数字段下面,我们发现了另一个字段,用于保存程序本身的参数。
这使得我们可以将go test 中的count 标志作为测试程序的test.count 标志,类似于GoLand添加test.v 和其他标志的方式:

而现在,我们确实得到了一个有问题的测试的双重运行:

剩下要做的就是实际调试了。应该不会太难!
