我的大多数代码都有单元测试。但我无法弄清楚如何为主包中的main()中的某些代码生成单元测试覆盖率。
主要功能非常简单。它基本上是一个选择块。它会读取标志,然后调用另一个函数/执行某些操作,或者只是在屏幕上打印帮助。但是,如果未正确设置命令行选项,它将以各种错误代码退出。因此,需要进行子流程测试。
我尝试了子流程测试技术,但修改了代码,使其包含覆盖标记:
cmd := exec.Command(os.Args[0], "-test.run=TestMain -test.coverprofile=/vagrant/ucover/coverage2.out")
以下是原始代码:https://talks.golang.org/2014/testing.slide#23 上述幻灯片的说明:http://youtu.be/ndmB0bj7eyw?t=47m16s
但它不会生成封面配置文件。我一直无法弄清楚为什么不。它确实为主进程执行测试生成封面配置文件,但是当然,在子进程中执行的任何代码都没有标记为已执行。
我尝试尽可能多地实现代码覆盖。我不确定我是否遗漏了某些东西,或者是否有更简单的方法可以做到这一点。或者如果不可能的话。
感谢任何帮助。
由于
阿梅尔
答案 0 :(得分:5)
我采用了另一种不涉及重构main()的方法:见this commit:
我使用全局(未导出)变量:
var args []string
然后在main()
中,我使用os.Args
,除非设置了私有var args
:
a := os.Args[1:]
if args != nil {
a = args
}
flag.CommandLine.Parse(a)
在我的测试中,我可以设置我想要的参数:
args = []string{"-v", "-audit", "_tests/p1/conf/gitolite.conf"}
main()
即使超过main()
,我仍然可以获得100%的代码覆盖率。
答案 1 :(得分:3)
我会将需要测试的逻辑考虑在main()
之外:
func main() {
start(os.Args)
}
func start(args []string) {
// old main() logic
}
通过这种方式,您可以在不改变start()
的情况下对os.Args
进行单元测试。
答案 2 :(得分:1)
在Go 1.11中使用@VonC解决方案,我发现我必须在每个测试中重设flag.CommandLine,以重新定义标志,以避免“标志重新定义”的恐慌。
for _, check := range checks {
t.Run("flagging " + check.arg, func(t *testing.T) {
flag.CommandLine = flag.NewFlagSet(cmd, flag.ContinueOnError)
args = []string{check.arg}
main()
})
}