最近一直在搭建 CI 框架,必不可少的就是需要有 Unit Test 的工具。
我们的代码90%都是C和C++,同时需要支持 Red Hat, SUSE, Windows和 Solaris 平台(好在Solaris 客户少了,对该版本的支持即将结束)。我们最终选定 Google Test 来做 UT。
首选从 github (https://github.com/google/googletest.git)上下载了 gtest 的源码.
按照网上大多数人的建议,cmake..... 本以为会很顺利的。结果却没有想象的那么简单。
查看 make 错误输出,发现提示 nullptr 未定义。OK, 了解了,这是因为 gtest 支持C++11。嗯,改吧...
这里走了一些弯路,之前我们用到的 gcc, git 这些都是官网上拿到最新版代码,按照提示直接 make, install。。我们想着 gtest 也这么做吧.
Gtest 的官网说了google 自己是没有用cmake 的,cmake 都是由社区贡献 。改cmake模板确实很麻烦,研究了一下 gtest 源码,发现没有必要这么做。
查看gtest 下 src 目录,发现有一个 gtest-all.cc文件。文件基本 include 目录下所有 .cc 的文件。gtest_main.cc 这里有 main 函数。那我们应该只需要编译这两个 cc 文件就行。
这是我的步骤:
1. g++ 编译 gtest-all.cc 文件,生成 gtest-all.o 文件
2. g++ 编译 gtest_main.cc 文件,生成 gtest_main.o 文件
3. 链接 gtest-all.o 为共享库 libgtest.so, 链接 gtest_main.o 为静态库gtest_main.a
4. libgtest.so 所在目录加到 LD_LIBRARY_PATH 变量中。
g++ -Wall -c -fPIC -std=c++11 -I./include -I./ -c ./src/gtest-all.cc g++ -I ../include -g -Wall -Wextra -pthread -c gtest_main.cc -std=c++11 g++ -shared -fPIC -o libgtest.so gtest-all.o ar rv gtest_main.a gtest_main.o
接下来就是使用 gtest了。
写一个简单函数先测试一下功能吧。这是我的目标函数,返回数的绝对值。
int myAbs(int x) { return x > 0 ? x : -x; }
测试目标函数,我至少需要三个case,以下是我的Utest 代码,正数返回
#include <gtest/gtest.h>
TEST(MyABSTest, PosNumber)
{
EXPECT_EQ(2, myAbs(2));
ASSERT_FALSE(myAbs(2) == -2);
}
TEST(MyABSTest, NegNumber)
{
EXPECT_EQ(2, myAbs(-2));
ASSERT_NE(myAbs(-2),-2);
}
TEST(MyABSTest, Zero)
{
EXPECT_EQ(0, myAbs(0));
}
编译代码的sample
-L/$(GTEST_LIB)/lib -lgtest -lpthread $(STATIC_LIBPATH)/gtest_main.a
需要需要指定 gtest 的共享库,以及 gtest_main.a 的静态库,不需要手动写 main 函数。
输出如下
这种纯文本输出肯定不是我想要的,因为我最后需要把输出结果放到Jenkins上的,所以需要输出为 xml. 执行参数为 ”--gtest_output=xml”
如果只简单指定输出为 xml, 会生成默认输出结果 test_detail.xml。也可以使用 "test_detail.xml:./testresult.xml" 在当前路径下生产 testresult.xml 的输出文件。
<?xml version="1.0" encoding="UTF-8"?> <testsuites tests="3" failures="0" disabled="0" errors="0" time="0.001" timestamp="2020-03-28T12:03:49" name="AllTests"> <testsuite name="MyABSTest" tests="3" failures="0" disabled="0" errors="0" time="0" timestamp="2020-03-28T12:03:49"> <testcase name="PosNumber" status="run" result="completed" time="0" timestamp="2020-03-28T12:03:49" classname="MyABSTest" /> <testcase name="NegNumber" status="run" result="completed" time="0" timestamp="2020-03-28T12:03:49" classname="MyABSTest" /> <testcase name="Zero" status="run" result="completed" time="0" timestamp="2020-03-28T12:03:49" classname="MyABSTest" /> </testsuite> </testsuites>
这样三个case 的 xml 格式输出正常了。
实际中,我需要为case添加一些描述信息,这样可以让后续维护的人了解每一个case大致都做些什么。
这时我需要修改我的测试代码,为其添加一些属性,代码如下
#include <gtest/gtest.h> #include "MyABS.h" TEST(MyABSTest, PosNumber) { this->RecordProperty("Description", "Pos number n return n"); EXPECT_EQ(2, myAbs(2)); ASSERT_FALSE(myAbs(2) == -2); } TEST(MyABSTest, NegNumber) { this->RecordProperty("Description", "Neg number n return -n"); EXPECT_EQ(2, myAbs(-2)); ASSERT_NE(myAbs(-2),-2); } TEST(MyABSTest, Zero) { this->RecordProperty("Description", "Zero return Zero"); EXPECT_EQ(0, myAbs(0)); }
输出xml 结果如下
<?xml version="1.0" encoding="UTF-8"?> <testsuites tests="3" failures="0" disabled="0" errors="0" time="0" timestamp="2020-03-28T12:12:29" name="AllTests"> <testsuite name="MyABSTest" tests="3" failures="0" disabled="0" errors="0" time="0" timestamp="2020-03-28T12:12:29"> <testcase name="PosNumber" status="run" result="completed" time="0" timestamp="2020-03-28T12:12:29" classname="MyABSTest"> <properties> <property name="Description" value="Pos number n return n"/> </properties> </testcase> <testcase name="NegNumber" status="run" result="completed" time="0" timestamp="2020-03-28T12:12:29" classname="MyABSTest"> <properties> <property name="Description" value="Neg number n return -n"/> </properties> </testcase> <testcase name="Zero" status="run" result="completed" time="0" timestamp="2020-03-28T12:12:29" classname="MyABSTest"> <properties> <property name="Description" value="Zero return Zero"/> </properties> </testcase> </testsuite> </testsuites>
好了, gtest 简单用法先介绍到这里了。会用 gtest 后,我们还需要学会 gmock。我会在下一篇文章中介绍 gmock 的使用。