在golang中使用子进程测试时如何生成单元测试覆盖率?

时间:2014-09-24 16:42:53

标签: unit-testing go

我的大多数代码都有单元测试。但我无法弄清楚如何为主包中的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

但它不会生成封面配置文件。我一直无法弄清楚为什么不。它确实为主进程执行测试生成封面配置文件,但是当然,在子进程中执行的任何代码都没有标记为已执行。

我尝试尽可能多地实现代码覆盖。我不确定我是否遗漏了某些东西,或者是否有更简单的方法可以做到这一点。或者如果不可能的话。

感谢任何帮助。

由于

阿梅尔

3 个答案:

答案 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()
        })
    }