Go 显示功能测试的覆盖范围,无盲点

Go 显示功能测试的覆盖范围,无盲点,go,Go,我有一个生产golang代码和功能测试,它不是用golang编写的。功能测试运行编译的二进制文件。我的产品代码的简化版本如下:main.go: package main import ( "fmt" "math/rand" "os" "time" ) func main() { rand.Seed(time.Now().UTC().UnixNano()) for {

我有一个生产golang代码和功能测试,它不是用golang编写的。功能测试运行编译的二进制文件。我的产品代码的简化版本如下:
main.go

package main

import (
    "fmt"
    "math/rand"
    "os"
    "time"
)

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    for {
        i := rand.Int()
        fmt.Println(i)
        if i%3 == 0 {
            os.Exit(0)
        }
        if i%2 == 0 {
            os.Exit(1)
        }
        time.Sleep(time.Second)
    }
}
package main

import (
    "flag"
    "fmt"
    "math/rand"
    "os"
    "runtime"
    "time"
)

var exitCh chan int = make(chan int)

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    for {
        i := rand.Int()
        fmt.Println(i)
        if i%3 == 0 {
            exit(0)
        }
        if i%2 == 0 {
            fmt.Println("status 1")
            exit(1)
        }
        time.Sleep(time.Second)
    }
}

func exit(code int) {
    if flag.Lookup("test.coverprofile") != nil {
        exitCh <- code
        runtime.Goexit()
    } else {
        os.Exit(code)
    }
}
我想为我的功能测试构建覆盖率配置文件。为此,我添加了
main\u test.go
文件,其中包含以下内容:

package main

import (
    "os"
    "testing"
)

var exitCode int

func Test_main(t *testing.T) {
    go main()
    exitCode = <-exitCh
}

func TestMain(m *testing.M) {
    m.Run()
    // can exit because cover profile is already written
    os.Exit(exitCode)
}
然后我构建覆盖率二进制文件:

go test -c -coverpkg=.  -o myProgram
然后我的功能测试运行覆盖率二进制文件,如下所示:

./myProgram -test.coverprofile=/tmp/profile
6507374435908599516
PASS
coverage: 64.3% of statements in .
我构建HTML输出,显示覆盖率:

$ go tool cover -html /tmp/profile -o /tmp/profile.html
$ open /tmp/profile.html

问题 方法
exit
将永远不会显示100%覆盖率,因为条件
if-flag.Lookup(“test.coverprofile”)!=无
。所以,行
os.Exit(code)
对于我的覆盖结果来说是一个盲点,尽管事实上,功能测试在这一行进行,这一行应该显示为绿色

另一方面,如果我删除条件
if flag.Lookup(“test.coverprofile”)!=nil
,行
os.Exit(code)
将在不构建覆盖率配置文件的情况下终止我的二进制文件

如何重写
exit()
以及可能的
main\u测试。转到
显示覆盖率,无盲点

想到的第一个解决方案是
time.Sleep()

func退出(代码int){

exitCh根据我们在评论中的对话,我们的覆盖率配置文件将永远不会包含该行代码,因为它永远不会被执行

如果看不到完整的代码,就很难找到合适的解决方案,但是在不牺牲太多的情况下,可以做一些事情来增加覆盖率

func Main和TestMain GOLANG的标准实践是避免测试主要的应用程序入口点,因此大多数专业人员将尽可能多的功能提取到其他类中,以便轻松测试

GOLANG
测试框架允许您在不使用main函数的情况下测试应用程序,但在测试框架中,您可以使用TestMain函数,该函数可用于测试代码需要在主线程上运行的位置。下面是一个小例子

测试程序有时需要在测试之前或之后进行额外的设置或拆卸。测试有时还需要控制哪些代码在
main
线程上运行。为了支持这些和其他情况,如果测试文件包含函数:
func TestMain(m*testing.m)

查看更多信息

工作示例 下面是一个测试代码所有功能的示例(覆盖率为93.3%,我们将使其达到100%)。我对您的设计做了一些更改,因为它不适合测试,但功能仍然相同

包干管

多芬奇

dofunc_test.go

梅因,加油

运行测试 如果我们使用封面配置文件构建应用程序

$go test-c-coverpkg=.-o示例

然后运行它

$。/示例-test.coverprofile=/tmp/profile

运行测试

1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 93.3% of statements in .
所以我们看到我们得到了93%的覆盖率,我们知道这是因为我们没有
main
的任何测试覆盖率。为了解决这个问题,我们可以为它编写一些测试(不是一个很好的主意),因为代码有
os.Exit
,或者我们可以重构它,使它非常简单,功能非常少,我们可以将它从测试中排除

要从覆盖率报告中排除
main.go
文件,我们可以通过在
main.go
文件的第一行放置标记注释来使用build
tags

/+build!测试

有关生成标志的更多信息,请查看此链接:

这将告诉
GOLANG
,如果存在标记构建,则文件应包含在构建过程中,但如果存在标记测试,则文件不应包含在构建过程中

请参阅完整代码

//+build !test

package main

import "os"

func main() {
    os.Exit(doFunc());
}
我们需要构建稍微不同的覆盖率应用程序

$go-test-c-coverpkg=.-o示例-tags-test

运行它也一样

$。/示例-test.coverprofile=/tmp/profile

我们得到下面的报告

1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 100.0% of statements in .
我们现在可以构建html的覆盖范围了

$go工具封面-html/tmp/profile-o/tmp/profile.html

在my中,我声明了一个包可见变量:

var exit = os.Exit
在设置测试的代码中,我用一个特定于测试的函数覆盖它,当拆掉一个测试时,我将它重置回os.Exit


这是一个简单实用的解决方案,对我来说效果很好,至少进行了一年的广泛测试。我得到了100%的分支覆盖率,因为根本没有分支参与。

我可以问一下达到100%和全部绿色的预期目的或好处是什么吗?如果一条线不是绿色,有什么不好?如果这是非技术性的魔法gement:只需通过对封面报告进行后处理,将有问题的行涂成绿色即可。@Volker只有一行不是绿色并不是致命的。我只是不想走捷径。如果没有其他选择,后处理听起来不错。运行您的测试几次,我每次都会得到不同的结果。此封面报告是在特定的运行中,而不是在整个测试过程中运行hing.我第一次得到70+/-%然后是80%等等@dmportella,因为应用程序使用随机数;如果删除随机数,每次运行时结果应该是一样的。我的问题是:如何在覆盖率结果中显示行
os.Exit(code)
已包含在内。但我意识到不进行后处理是不可能的。想象一下
os.Exit(code)
变为绿色,这意味着应用程序执行了它,但它意味着它在不构建覆盖率配置文件的情况下立即退出。因此,即使我的功能测试覆盖了这一行,我们也永远不会在覆盖率结果中看到这一行绿色。我使用的正是你的代码,但覆盖率为93.3%。
无论如何。看起来像
/+build!test
-标签测试
1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 93.3% of statements in .
//+build !test

package main

import "os"

func main() {
    os.Exit(doFunc());
}
1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 100.0% of statements in .
var exit = os.Exit