Go 显示功能测试的覆盖范围,无盲点
我有一个生产golang代码和功能测试,它不是用golang编写的。功能测试运行编译的二进制文件。我的产品代码的简化版本如下:Go 显示功能测试的覆盖范围,无盲点,go,Go,我有一个生产golang代码和功能测试,它不是用golang编写的。功能测试运行编译的二进制文件。我的产品代码的简化版本如下:main.go: package main import ( "fmt" "math/rand" "os" "time" ) func main() { rand.Seed(time.Now().UTC().UnixNano()) for {
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
文件的第一行放置标记注释来使用buildtags
/+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