使用gtest + Gcov 对C++ project进行单元测试以及单测覆盖率统计


一、背景

单元测试对于开发人员来讲很普通但又经常被忽视,但是并不代表单元测试不重要相反很有必要,列举几条必要性的原因a

总之,提前使用起来会让后面的开发维护工作更加轻松

二、gtest使用方法

详细介绍参考官方文档:https://github.com/google/googletest/blob/master/googletest/docs/primer.md
一下的内容以我自己写的一个cpp project为例展开
第一步 安装gtest
mac版本: http://ysonggit.github.io/system/2015/01/01/install-gtest-on-mac-os-yosemite.html
第二步,编写单测
提前知晓:程序单测的对象主要是我的c++ project里写的类方法,这里我只有一个头文件,封装了我的project里使用的大部分方法。如下是步骤。

例子:
我这里有一个Data.h

#ifndef Data_hpp
#define Data_hpp

#include <stdio.h>
#include <vector>
#include <string>
#include <boost/regex.hpp>

class Data {
private:
    //std::string context;
    std::string week_today;
    std::string data_today;
    std::string weather_today;
    std::string temperature_high_today;
    std::string temperature_low_today;
    std::string week_tomorrow;
    std::string data_tomorrow;
    std::string weather_tomorrow;
    std::string temperature_hight_tomorrow;
    std::string temperature_low_tomorrow;
    boost::smatch _what;
    boost::regex _reg;

public:
    //delete the html's tag extract the contex of the page
    std::string replace(std::string context);

    //set reg
    void setReg(std::string reg);

    //select the context from the pages by reg
    std::string filterString(std::string context);

    //delete the spaces of the pages make sure the context is a type of string which have no spaces
    std::string deleteSpaceContext(std::string context);

    // data Normalization
    std::string dataNormalization(std::string context);

    void fillData(int index_week_today, std::string context);

};

#endif /* Data_hpp */


现在要对Data.h写单元测试,一共有六个类函数,每个类函数有不同的输入与输出
unitTest.cpp 内容

#include <iostream>
#include <gtest/gtest.h>
#include "Data.hpp"
#include <string>


std::string test_replace(std::string html) {
    Data data;
    std::string ret = data.replace(html);
    return ret;
}

std::string test_filterString(std::string context, std::string test_reg) {
    Data data;
    data.setReg(test_reg);
    std::string ret = data.filterString(context);
    return ret;
}

std::string test_deleteSpaceContext(std::string context) {
    Data data;
    std::string ret = data.deleteSpaceContext(context);
    return ret;
}


std::string test_dataNormalization(std::string context){
    Data data;
    std::string ret = data.dataNormalization(context);
    return ret;
}



TEST(testCase, test0) {
    std::string str_repalce = "haha";
    EXPECT_EQ(str_repalce, test_replace("<div>haha</div>"));
}

TEST(testCase, test1) {
    std::string str_replace = "\n18\n\n\n东风\n";
    EXPECT_EQ(str_replace, test_replace("<div class=\"temp\">\n18\n</div>\n<div class=\"direct\">\n东风\n</div>"));
}

TEST(testCase, test2) {
    std::string str_filter = "<div class=\"temp\">\n18\n</div>";
    std::string test_reg = "<.*?18\n</div>";
    EXPECT_EQ(str_filter, test_filterString("<div class=\"temp\">\n18\n</div>\n<div class=\"direct\">\n东风\n</div>", test_reg));

}

TEST(testCase, test3) {
    std::string str_deleteSpace = "嘻嘻哈哈";
    EXPECT_EQ(str_deleteSpace, test_deleteSpaceContext("嘻嘻 哈哈"));
}

TEST(testCase, test4) {
    std::string str_deletespace = "嘻嘻哈哈";
    EXPECT_EQ(str_deletespace, test_deleteSpaceContext("嘻 嘻\n哈哈"));
}

TEST(testCase, test5) {
    std::string str_dataNorm = "2018晴天晴天";
    EXPECT_EQ(str_dataNorm, test_dataNormalization("2018晴晴"));
}

TEST(testCase, test6){
    std::string str_dataNorm = "晴天阴天";
    EXPECT_EQ(str_dataNorm, test_dataNormalization("晴阴"));
}

TEST(testCase, test7) {
    std::string str_dataNorm = "晴天小雨";
    EXPECT_EQ(str_dataNorm, test_dataNormalization("晴小雨"));
}


int main(int argc, char *argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    testing::InitGoogleTest(&argc, argv);
    //LOG(FATAL)<<"FATAL";
    return RUN_ALL_TESTS();
}

main函数里的 testing::InitGoogleTest(&argc, argv); 是入口,case里是EXPECT_EQ判断是否相等,当然还有其他方式的断言,可以参考官方文档

第三步,编译执行单测文件unitTest
就可以看到测试结果 如下图我写了8个单元测试case 全部passed。

三、单元测试覆盖率统计

做好了单元测试但是别人并不知道我们的单元测试做的如何,是否覆盖了所有的需要被测试的类方法或者变量等,所以我们需要对单测case做一个覆盖率统计。其实本质就是看代码执行时候运行了你需要测试文件里面的所有代码,比如switch分支 if分支等。
步骤:
1、安装lcov mac下 brew install lcov
2、重新编译单元测试文件unitTest.cpp 加上-fprofile-arcs -ftest-coverage选项 编译后会生成.gcda .gcno文件
3、运行刚刚编译生成的执行文件
4、执行lcov -d . -t ‘unittest’ -o ‘test.info’ -b . -c

5、执行 genhtml -o result test.info 生成html文件可视化

打开生成的result文件 index.html就可以看到覆盖率统计情况
使用gtest + Gcov 对C++ project进行单元测试以及单测覆盖率统计-LMLPHP

10-07 15:11