编写能提升覆盖率的代码
代码的覆盖率不是测试出来的吗?为什么还能通过代码本身提高代码的覆盖率?
事情是这样,最近产品要发布,要对代码进行覆盖率测试,就是运行实际的业务,看代码最终的覆盖情况。用到的工具是 gcov。
这样的话,要代码覆盖率高,那么我们就要尽量覆盖到所有的情况,正常业务,以及代码中的一场分支。所以为了提升覆盖率,那么自然的是,一个业务操作能尽量高的覆盖率,避免构建各种复杂的情况进行覆盖,这样工作效率就上不去了。
我们来看段代码:
void HttpRequestError(enum evhttp_request_error error, void *arg)
{
switch (error) {
case EVREQ_HTTP_TIMEOUT:
printf("HTTP requests timed out\n");
break;
case EVREQ_HTTP_EOF:
printf("Premature end of HTTP response\n");
break;
case EVREQ_HTTP_INVALID_HEADER:
printf("Invalid HTTP header encountered\n");
break;
case EVREQ_HTTP_BUFFER_ERROR:
printf("HTTP buffer related error\n");
break;
default:
printf("HTTP requests failed with error code %d \n", error);
break;
}
}
这是使用 libevent 库时,设置的一个错误回调函数,根据不同的错误码打印不同的消息,这样的代码,我们要覆盖所有的分支是比较难的,因为要模拟各种异常的情况。一般模拟个超时是比较容易的。
去掉 libevent 引用部分,我们改下上面的代码,如下:
#include <stdio.h>
#define EVREQ_HTTP_TIMEOUT 0
#define EVREQ_HTTP_EOF 1
#define EVREQ_HTTP_INVALID_HEADER 2
#define EVREQ_HTTP_BUFFER_ERROR 3
void HttpRequestError(int error)
{
switch (error) {
case EVREQ_HTTP_TIMEOUT:
printf("HTTP requests timed out\n");
break;
case EVREQ_HTTP_EOF:
printf("Premature end of HTTP response\n");
break;
case EVREQ_HTTP_INVALID_HEADER:
printf("Invalid HTTP header encountered\n");
break;
case EVREQ_HTTP_BUFFER_ERROR:
printf("HTTP buffer related error\n");
break;
default:
printf("HTTP requests failed with error code %d \n", error);
break;
}
}
int main()
{
HttpRequestError(EVREQ_HTTP_TIMEOUT);
return 0;
}
makefile 文件如下:
CFLAGS+= -fprofile-arcs -ftest-coverage -fbranch-probabilities
CC=g++
all: helloworld
helloworld: *.cpp
@${CC} ${CFLAGS} -o helloworld_gcov converage.cpp
converage:
echo "result."
gcov -c -d . -o helloworld_gcov.info
lcov -c -d . -o helloworld_gcov.info --rc lcov_branch_coverage=1
genhtml -o result --branch-coverage helloworld_gcov.info
clean:
rm -rf *.gcno *.gcda
先 make 编译得到 可执行文件,然后运行可执行文件,会得到 gcno 以及 gcda 文件,最后 make converage, 会得到 result 目录,打开里面的 index.html,可以看到覆盖率的详细信息:

| Lines: | 8 | 16 | 50.0 % | |||
| Functions: | 2 | 2 | 100.0 % | |||
| Branches: | 1 | 5 | 20.0 % |
和预期的一样,只覆盖到了 EVREQ_HTTP_TIMEOUT 分支。
我们用 map 重构下代码:
#include <stdio.h>
#include <map>
#include <string>
using namespace std;
#define EVREQ_HTTP_TIMEOUT 0
#define EVREQ_HTTP_EOF 1
#define EVREQ_HTTP_INVALID_HEADER 2
#define EVREQ_HTTP_BUFFER_ERROR 3
#define EVREQ_DEFAULT 4
std::map<int, string> mm = {
{EVREQ_HTTP_TIMEOUT, "HTTP requests timed out\n"},
{EVREQ_HTTP_EOF, "Premature end of HTTP response\n"},
{EVREQ_HTTP_INVALID_HEADER, "Invalid HTTP header encountered\n"},
{EVREQ_HTTP_BUFFER_ERROR, "HTTP buffer related error\n"},
{EVREQ_DEFAULT, "HTTP requests failed with error code %d \n"}
};
void HttpRequestError(int error)
{
string out = mm[EVREQ_DEFAULT];
if (mm.find(error)!= mm.end())
{
out = mm[error];
}
printf(out.c_str(), error);
}
int main()
{
HttpRequestError(EVREQ_HTTP_TIMEOUT);
return 0;
}
再次看下覆盖率:

| Lines: | 12 | 12 | 100.0 % | |||
| Functions: | 4 | 4 | 100.0 % | |||
| Branches: | 17 | 34 | 50.0 % |
行覆盖率和分支覆盖率都有所提升。
回顾上面的代码,只要跑一种异常情况,就覆盖了所有的代码。
这里的做法就是把一样的逻辑,通过 map 进行封装,压缩成了只有一种情况了。
还有没有其他代码的写法也能类似的提升覆盖率呢?