显示没有盲点的功能测试的覆盖范围 [英] showing coverage of functional tests without blind spots

查看:99
本文介绍了显示没有盲点的功能测试的覆盖范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个生产golang代码和功能测试,它不是写在golang中的。功能测试运行编译的二进制文件我的生产代码非常简化版本在这里: main.go

 包主要

进口(
fmt
数学/兰特
os
时间


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)
}
}

我想为我的功能测试构建覆盖范围配置文件。为了做到这一点,我添加了 main_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()
//可以退出,因为封面配置文件已经写入
os.Exit(exitCode)
}

并修改 main.go

  package main 

导入(
flag
fmt
math / rand
os
runtime
time


var exitCh chan int = make(chan int)

func ()
rand.Seed(time.Now()。UTC()。UnixNano())
for {
i:= rand.Int()
fmt.Println (i)
if i%3 == 0 {
exit(0)
}
if i%2 == 0 {
fmt.Printl n(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)
}
}

然后我构建二进制覆盖:

  go test -c -coverpkg =。 -o myProgram 

然后我的功能测试运行这个覆盖二进制文件,如下所示:

  ./ myProgram -test.coverprofile = / tmp / profile 
6507374435908599516
PASS
覆盖率:报表的64.3%中。

我构建HTML输出以显示覆盖范围:

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



问题



如果flag.Lookup(test。)方法 exit 不会显示100%覆盖率,因为条件 coverprofile)!= nil 。因此,对于我的覆盖率结果,行 os.Exit(code)是一个盲点,但事实上,功能测试会在此行上显示,并且此行应显示为绿色。另一方面,如果我删除条件,如果flag.Lookup(test.coverprofile)!= nil ,$ os.Exit(code)这行会终止我的二进制文件,如何重写 exit()或者 main_test.go 来显示没有盲点的报道



首先想到的解决方案是 time.Sleep()

  func exit(code int){
exitCh< - code
time.Sleep(time.Second)/ /等待一段时间让coverprofile写入
os.Exit(code)
}
}

但它不是很好,因为在退出之前会导致产品代码变慢。

解决方根据我们在评论中的谈话,我们的报道概况将永远不会包含该行代码,因为它永远不会被执行。



你的完整代码很难提出适当的解决方案,但是你可以做一些事情来增加覆盖率,而不用牺牲太多。

func Main和TestMain对于 GOLANG 的标准做法是为了避免测试主应用程序入口点,以便大多数专业人员可以将许多功能提取到其他功能中类,因此它们可以很容易地测试。



GOLANG 测试框架允许你测试你的应用程序,但是在这个地方你可以使用TestMain函数,它可以用来测试代码需要在主线程上运行的位置。以下是


I have a production golang code and functional tests for it written not in golang. Functional tests run compiled binary. Very simplified version of my production code is here: 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)
    }
}

I want to build coverage profile for my functional tests. In order to do it I add main_test.go file with content:

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)
}

And modify main.go:

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)
    }
}

Then I build coverage binary:

go test -c -coverpkg=.  -o myProgram

Then my functional tests run this coverage binary, like this:

./myProgram -test.coverprofile=/tmp/profile
6507374435908599516
PASS
coverage: 64.3% of statements in .

And I build HTML output showing coverage:

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

Problem

Method exit will never show 100% coverage because of condition if flag.Lookup("test.coverprofile") != nil. So line os.Exit(code) is kinda blind spot for my coverage results, although, in fact, functional tests go on this line and this line should be shown as green.

On the other hand, if I remove condition if flag.Lookup("test.coverprofile") != nil, the line os.Exit(code) will terminate my binary without building coverage profile.

How to rewrite exit() and maybe main_test.go to show coverage without blind spots?

The first solution that comes into mind is time.Sleep():

func exit(code int) {
        exitCh <- code
        time.Sleep(time.Second) // wait some time to let coverprofile be written
        os.Exit(code)
    }
}

But it's not very good because will cause production code slow down before exit.

解决方案

As per our conversation in the comments our coverage profile will never include that line of code because it will never be executed.

With out seeing your full code it is hard to come up with a proper solutions however there a few things you can do to increase the coverage with out sacrificing too much.

func Main and TestMain

Standard practice for GOLANG is to avoid testing the main application entry point so most professionals extract as much functionality into other classes so they can be easily tested.

GOLANG testing framework allows you to test your application with out the main function but in it place you can use the TestMain func which can be used to test where the code needs to be run on the main thread. Below is a small exert from GOLANG Testing.

It is sometimes necessary for a test program to do extra setup or teardown before or after testing. It is also sometimes necessary for a test to control which code runs on the main thread. To support these and other cases, if a test file contains a function: func TestMain(m *testing.M)

Check GOLANG Testing for more information.

Working example

Below is an example (with 93.3% coverage that we will make it 100%) that tests all the functionality of your code. I made a few changes to your design because it did not lend itself very well for testing but the functionality still the same.

package main

dofunc.go

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

var seed int64 = time.Now().UTC().UnixNano()

func doFunc() int {
    rand.Seed(seed)
    var code int
    for {
        i := rand.Int()
        fmt.Println(i)
        if i%3 == 0 {
            code = 0
            break
        }
        if i%2 == 0 {
            fmt.Println("status 1")
            code = 1
            break
        }
        time.Sleep(time.Second)
    }
    return code
}

dofunc_test.go

package main

import (
    "testing"
    "flag"
    "os"
)

var exitCode int

func TestMain(m *testing.M) {
    flag.Parse()
    code := m.Run()
    os.Exit(code)
}

func TestDoFuncErrorCodeZero(t *testing.T) {
    seed = 2

    if code:= doFunc(); code != 0 {
        t.Fail()
    }
}

func TestDoFuncErrorCodeOne(t *testing.T) {
    seed = 3

    if code:= doFunc(); code != 1 {
        t.Fail()
    }
}

main.go

package main

import "os"

func main() {
    os.Exit(doFunc());
}

Running the tests

If we build our application with the cover profile.

$ go test -c -coverpkg=. -o example

And run it.

$ ./example -test.coverprofile=/tmp/profile

Running the tests

1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 93.3% of statements in .

So we see that we got 93% coverage we know that is because we don't have any test coverage for main to fix this we could write some tests for it (not a very good idea) since the code has os.Exit or we can refactor it so it is super simple with very little functionality we can exclude it from our tests.

To exclude the main.go file from the coverage reports we can use build tags by placing tag comment at the first line of the main.go file.

//+build !test

For more information about build flags check this link: http://dave.cheney.net/2013/10/12/how-to-use-conditional-compilation-with-the-go-build-tool

This will tell GOLANG that the file should be included in the build process where the tag build is present butNOT where the tag test is present.

See full code.

//+build !test

package main

import "os"

func main() {
    os.Exit(doFunc());
}

We need need to build the the coverage application slightly different.

$ go test -c -coverpkg=. -o example -tags test

Running it would be the same.

$ ./example -test.coverprofile=/tmp/profile

We get the report below.

1543039099823358511
2444694468985893231
6640668014774057861
6019456696934794384
status 1
PASS
coverage: 100.0% of statements in .

We can now build the coverage html out.

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

这篇关于显示没有盲点的功能测试的覆盖范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆