go语言快速入门:测试覆盖率(18)

上篇文章讨论了如何使用testing标准包进行自动化测试,在这篇文章中将进一步细化测试覆盖率的可视化确认。

测试覆盖率

代码测试的覆盖率分很多种,语句覆盖/条件覆盖等等,而在go中,测试覆盖率是这样定义的:Test coverage is a term that describes how much of a package’s code is exercised by running the package’s tests. 可以看出,go语言中的理解是:测试覆盖率是通过执行某包的测试用例来确认代码被执行的程度的术语。

项目 URL
Cover https://blog.golang.org/cover

统计模式

在go语言的测试覆盖率统计时,go test通过参数covermode的设定可以对覆盖率统计模式作如下三种设定。

模式 解释
set 缺省模式, 只记录语句是否被执行过
count 记录语句被执行的次数
atomic 记录语句被执行的次数,并保证在并发执行时的正确性

测试对象

项目 详细 说明
文件名 basicfunc.go 测试对象文件
package名 basicfunc 测试对象Package
测试对象函数 GetGrade 输入分数返回A-D的等级
测试对象函数 Add 输入两个数字,返回其相加之和

示例代码

[root@liumiaocn test]# cat basicfunc.go
package basicfunc

func GetGrade(score int) string {
        switch {
        case score < 60:
                return "D"
        case score <= 70:
                return "C"
        case score <= 80:
                return "B"
        case score <= 90:
                return "A"
        default:
                return "Undefined"
        }
}

func Add(num1 int, num2 int) int {
        return num1 + num2
}
[root@liumiaocn test]#

功能测试用例

[root@liumiaocn test]# cat func_test.go
package basicfunc

import "testing"

func TestBasic(test *testing.T) {
        grade := GetGrade(40)
        if grade != "D" {
                test.Error("Test Case failed.")
        }

}

func TestAddfunc(test *testing.T) {
        sum := Add(1, 1)
        if sum == 2 {
                test.Log("Passed: 1 + 1 == 2 ")
        } else {
                test.Log("Failed: 1 + 1 == 2 ")
        }
}
[root@liumiaocn test]#

覆盖率确认

注意事项

当go test命令无法正常运行时,学习时请将测试对象package放到GOROOT下,比如

[root@liumiaocn test]# echo $GOROOT
/usr/local/go
[root@liumiaocn test]# pwd
/usr/local/go/src/goprj/test
[root@liumiaocn test]#

确认覆盖率

[root@liumiaocn test]# go test -cover
PASS
coverage: 42.9% of statements
ok      goprj/test      0.007s
[root@liumiaocn test]#

HTML方式

可以看到我们现在得到了一个42.9%的覆盖率,这说明42.9%的代码在测试用例执行中被至少执行过一次。但是想确认那些语句没有被执行的时候,上述方法就无法使用了。接下来我们将继续使用go test命令生成html文件用以显示测试覆盖率情况。

生成HTML前的准备

哪个文件的哪行代码被执行了的中间信息如果没有的话,无论什么工具也会无能为力。所以第一步就是要生成这些中间的信息。

[root@liumiaocn test]# pwd
/usr/local/go/src/goprj/test
[root@liumiaocn test]# go test -coverprofile=covprofile
PASS
coverage: 42.9% of statements
ok      goprj/test      0.007s
[root@liumiaocn test]# cat covprofile
mode: set
goprj/test/basicfunc.go:3.33,4.16 1 1
goprj/test/basicfunc.go:5.9,6.27 1 1
goprj/test/basicfunc.go:7.9,8.27 1 0
goprj/test/basicfunc.go:9.9,10.27 1 0
goprj/test/basicfunc.go:11.9,12.27 1 0
goprj/test/basicfunc.go:13.9,14.35 1 0
goprj/test/basicfunc.go:18.34,20.2 1 1
[root@liumiaocn test]#

可以看到生成的中间文件covprofile中覆盖率统计模式为set,同时还有很多统计信息。

生成HTML文件

[root@liumiaocn test]# go tool cover -html=covprofile -o coverage.html
[root@liumiaocn test]#

生成的HTML文件内容

[root@liumiaocn test]# cat coverage.html

<!DOCTYPE html>
<html>
        <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
                <style>
                        body {
                                background: black;
                                color: rgb(80, 80, 80);
                        }
                        body, pre, #legend span {
                                font-family: Menlo, monospace;
                                font-weight: bold;
                        }
                        #topbar {
                                background: black;
                                position: fixed;
                                top: 0; left: 0; right: 0;
                                height: 42px;
                                border-bottom: 1px solid rgb(80, 80, 80);
                        }
                        #content {
                                margin-top: 50px;
                        }
                        #nav, #legend {
                                float: left;
                                margin-left: 10px;
                        }
                        #legend {
                                margin-top: 12px;
                        }
                        #nav {
                                margin-top: 10px;
                        }
                        #legend span {
                                margin: 0 5px;
                        }
                        .cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

                </style>
        </head>
        <body>
                <div id="topbar">
                        <div id="nav">
                                <select id="files">

                                <option value="file0">goprj/test/basicfunc.go (42.9%)</option>

                                </select>
                        </div>
                        <div id="legend">
                                <span>not tracked</span>

                                <span class="cov0">not covered</span>
                                <span class="cov8">covered</span>

                        </div>
                </div>
                <div id="content">

                <pre class="file" id="file0" style="display: none">package basicfunc

func GetGrade(score int) string <span class="cov8" title="1">{
        switch </span>{
        <span class="cov8" title="1">case score &lt; 60:
                return "D"</span>
        <span class="cov0" title="0">case score &lt;= 70:
                return "C"</span>
        <span class="cov0" title="0">case score &lt;= 80:
                return "B"</span>
        <span class="cov0" title="0">case score &lt;= 90:
                return "A"</span>
        <span class="cov0" title="0">default:
                return "Undefined"</span>
        }
}

func Add(num1 int, num2 int) int <span class="cov8" title="1">{
        return num1 + num2
}</span>
</pre>

                </div>
        </body>
        <script>
        (function() {
                var files = document.getElementById('files');
                var visible;
                files.addEventListener('change', onChange, false);
                function select(part) {
                        if (visible)
                                visible.style.display = 'none';
                        visible = document.getElementById(part);
                        if (!visible)
                                return;
                        files.value = part;
                        visible.style.display = 'block';
                        location.hash = part;
                }
                function onChange() {
                        select(files.value);
                        window.scrollTo(0, 0);
                }
                if (location.hash != "") {
                        select(location.hash.substr(1));
                }
                if (!visible) {
                        select("file0");
                }
        })();
        </script>
</html>
[root@liumiaocn test]#

结果确认

将生成的HTML用浏览器打开,可以很清楚地看到测试用例覆盖的代码和未曾覆盖到的代码,一目了然。
这里写图片描述

参考文献

项目 URL
Cover https://blog.golang.org/cover
展开阅读全文

如何在自定义文件夹中使用go test生成多个软件包的覆盖率

03-26
<div class="post-text" itemprop="text"> <p>We have following project structure:</p> <pre><code>├── Makefile ├── ... ├── src │   ├── app │   │   ├── main.go │ │ ├── models │ │ ├── ... │ │ └── dao.go │   │   ├── ... │   │   └── controllers │ │ ├── ... │ │ └── pingController.go │   └── test │   ├── all_test.go │   ├── ... │   └── controllers_test.go └── vendor └── src ├── github.com ├── golang.org └── gopkg.in </code></pre> <p>I want to measure coverage of packages in src/app by tests in src/test. And currently generating coverage profile by running custom script that runs coverage for each package in app and then merges all coverage profiles into one file. Recently I heard that in go1.10 we are able to generate coverage for multiple packages.</p> <p>So I tried to replace that script with oneliner, and tried running</p> <pre><code>GOPATH=${PROJECT_DIR}:${PROJECT_DIR}/vendor go test -covermode count -coverprofile cover.out -coverpkg all ./src/test/... </code></pre> <p>It gives me "ok test 0.475s coverage: 0.0% of statements in all"</p> <p>When I do </p> <pre><code>cd src/test/ GOPATH=${PROJECT_DIR}:${PROJECT_DIR}/vendor go test -covermode count -coverprofile cover.out -coverpkg all </code></pre> <p>Logs show that specs are runned and tests are successfull, but still I have "coverage: 0.0% of statements in all" and empty cover.out.</p> <p>What am I missing to properly compute coverage of packages in app by tests in test? </p> </div>

无法生成Go Coverage文件

07-23
<div class="post-text" itemprop="text"> <p>I tried run a sytemTest in this article: <a href="https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests" rel="nofollow noreferrer">https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests</a></p> <p>so follow the tips first I create a system test file named main_test.go like this:</p> <pre><code>func TestSystem(t *testing.T) { t.Logf("systemtest mod=%v", *SystemTest) if *SystemTest { t.Log("runing system test....") main() } } </code></pre> <p>when this unit test is executed, whole main function will be executed</p> <p>then I build a test birnary :</p> <pre><code>go test -c -covermode=count -coverpkg ./... -o main.test </code></pre> <p>and execute the test birnary file in my test environment</p> <pre><code>./main.test -systemTest -test.coverprofile ./coverage.cov </code></pre> <p>because the program will listen and waiting for client's request, so it will not exit unless I exit manunal, which means the coverprofile won't be genarated</p> <p>so I start a timer to stop the program after 15 seconds... however when program exits , the coverprofile still not genarated</p> <p>if test not call main, the coverprofile can be genarated normally</p> <p>See the main function </p> <pre><code>var mkrtExitWait sync.WaitGroup var mkrtExitCode int var mkrtRunning bool = false func MKrtRun() int { mkrtExitWait.Add(1) mkrtRunning = true mkrtExitWait.Wait() return mkrtExitCode } func MKrtExit(code int) { if !mkrtRunning { os.Exit(code) } else { mkrtRunning = false mkrtExitCode = code mkrtExitWait.Done() } } func main() { // listen and serve code ...... if *SystemTest { // a command flag go func(){ time.Sleep(time.Second * 10) MKrtExit(0) }() } MKrtRun() } </code></pre> <p>I tried some ways to genrate coverage file as followed, but it's not working:</p> <ol> <li><p>send a client request to tell test server to execute os.Exit(0) when program is running</p></li> <li><p>send a client request to tell test server to execute panic() when program is running</p></li> <li><p>kill the process to force exit the program</p></li> </ol> <p>What's the problem?</p> <p>How can I generate coverage file?</p> </div>
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值