Ubuntu16.04 编译和安装googletest出错解决,gtest入门基本使用介绍,必备测试框架gtest官方文档简介
发表于: 2020-05-12 19:34:00 | 已被阅读: 72 | 分类于: 系统部署
gtest(googletest)是什么?
gtest是一个跨平台的(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)C++单元测试框架,由 Google 公司发布。gtest 是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。 一句话就是 gtest 提供了一种自动化测试程序的框架。方便快速的验证程序的逻辑性和健壮性。
下载、编译和安装 gtest
gtest 的源代码可以从GitHub下载到,在 Ubuntu 16.04 下执行下面这条 git 命令即可完成下载:
$ git clone https://github.com/google/googletest
下载后进入工程,不难发现 googletest 是支持 cmake 编译的,因此执行下面的命令:
$ cd googletest
$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=<安装路径> ..
$ make
在我的机器上,出现了许多错误:
...
At global scope:
/lccRoot/xx_test/googletest/googletest/src/gtest.cc:4082:13: error: ‘bool testing::internal::PortableLocaltime(time_t, tm*)’ defined but not used [-Werror=unused-function]
static bool PortableLocaltime(time_t seconds, struct tm* out) {
^
cc1plus: all warnings being treated as errors
googletest/CMakeFiles/gtest.dir/build.make:62: recipe for target 'googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o' failed
make[2]: *** [googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o] Error 1
CMakeFiles/Makefile2:184: recipe for target 'googletest/CMakeFiles/gtest.dir/all' failed
make[1]: *** [googletest/CMakeFiles/gtest.dir/all] Error 2
Makefile:138: recipe for target 'all' failed
make: *** [all] Error 2
往上翻一翻,发现许多相同的错误:
...
error: ‘nullptr’ was not declared in this scope
...
error: ‘auto’ changes meaning in C++11
...
看到这些错误,猜测是需要提供 C++11 的支持,因此修改 CMakeLists.txt,添加如下代码:
set(CMAKE_CXX_FLAGS "-std=c++11")
保存后,再重新执行上述过程,发现编译成功了。执行
$ make install
即可把编译好的 gtest 安装到我们指定的目录。接下来,读者可先从文章末尾的C++实例阅读,然后再回头阅读这些基本概念就轻松许多了。
googletest 的 assertions
gtest 的 test 由一些看起来很像函数调用的宏组成,测试函数或者类就是通过这些 test进行的。当某个 test 失败时,gtest 会输出出错位置所在的源文件名、行号,以及错误信息,用户也可以额外添加一些自己的信息。
gtest 主要有两种不同的 test:ASSERT_*
前缀的测试失败时,会 abort(终止) 当前的功能测试。EXPECT_*
前缀的测试失败时,会产生非致命的错误——也即不会终止当前功能测试。一般来说,EXPECT_*
前缀的测试用的更多,因为它们能够找到多个可能的错误信息。当然了,如果某项错误会导致整个流程无法进行,无疑是应该使用 ASSERT_*
前缀的测试的。
应该注意,
ASSERT_*
失败时会立刻终止测试,可能会跳过一些 clean-up 代码,这会带来一些内存泄漏。
下面是一段C++代码示例,该例子添加了额外的自定义信息:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
显然,添加自定义的额外信息是非常简单的,把 gtest 当作 std::cout 一样,使用“<<”符号即可。
基本测试
请看下表:
致命测试 | 非致命测试 | 期望值 |
---|---|---|
ASSERT_TRUE(cond) | EXPECT_TRUE(cond) | cond为真 |
ASSERT_FALSE(cond) | EXPECT_FALSE(cond) | cond为假 |
应明白,当 cond 不为期望值时,ASSERT_* 会输出错误信息,并立刻终止当前测试;而 EXPECT_* 则仅输出错误信息,测试将继续往下进行。
值比较
请看下表:
致命测试 | 非致命测试 | 期望值 |
---|---|---|
ASSERT_EQ(val1, val2) | EXPECT_EQ(val1, val2) | val1 == val2 |
ASSERT_NE(val1, val2) | EXPECT_NE(val1, val2) | val1 != val2 |
ASSERT_LT(val1, val2) | EXPECT_LT(val1, val2) | val1 < val2 |
ASSERT_LE(val1, val2) | EXPECT_LE(val1, val2) | val1 <= val2 |
ASSERT_GT(val1, val2) | EXPECT_GT(val1, val2) | val1 > val2 |
ASSERT_GE(val1, val2) | EXPECT_GE(val1, val2) | val1 >= val2 |
使用这些测试必须保证参数可以比较,否则将会得到编译错误。和之前的例子一样,上表中的测试宏也能够使用“<<”符号向 ostream 输出自定义的信息。但是 google 做了很好的封装,为了方便,通常情况下我们不需要自定义错误信息,gtest 会尽力以最好的方式输出便于调试的信息。
gtest 还提供了 ASSERT_TRUE() 和 EXPECT_TRUE() 宏,这两个宏可以测试用户自定义的判断语句,例如:
ASSERT_TRUE(actual == expected);
上面这行代码大多数情况下等价于 ASSERT_EQ(actual, expected),当二者等价时,更推荐后面那种写法。因为 ASSERT_EQ() 宏在出错时,会自动输出 actual 和 expected 的值,而 ASSERT_TRUE() 宏则不会。
上表里的宏和C/C++中的其他普通函数一样,参数的 side-effect 起作用的顺序是未定义的(C++编译器按照随机顺序执行参数)。
注意,ASSERT_EQ() 用于测试C语言中的指针时,它仅比对指针本身的值,而不是比对指针指向的内容,如果希望比对内容,应该使用 ASSERT_STREQ()。如果比对C++中的 string 对象,则应该使用 ASSERT_EQ()。
比对空指针时,更推荐
*_EQ(ptr, nullptr)
和*_NE(ptr, nullptr)
而不是*_EQ(ptr, NULL)
和*_NE(ptr, NULL)
。
C语言字符串比较
这里讨论的是C语言风格的字符串,如果是比对C++中的 string 对象,应该使用前面提到的 *_EQ
和 *_NE
。
致命测试 | 非致命测试 | 期望值 |
---|---|---|
ASSERT_STREQ(str1,str2) | EXPECT_STREQ(str1,str2) | str1和str2指向的内容相同 |
ASSERT_STRNE(str1,str2) | EXPECT_STRNE(str1,str2) | str1和str2指向的内容不同 |
ASSERT_STRCASEEQ(str1,str2) | EXPECT_STRCASEEQ(str1,str2) | str1和str2指向的内容相同,忽略大小写 |
ASSERT_STRCASENE(str1,str2 | EXPECT_STRCASENE(str1,str2) | str1和str2指向的内容不同,忽略大小写 |
简单实例
创建一个测试:
- 使用
TEST()
宏定义和命名一个测试单元,这个过程很像定义没有返回值的函数。 - 在 TEST() 功能区内可以编写任意的合法C++代码,使用前面讨论的各种测试宏测试功能。
- 测试结果取决于 TEST() 功能区的各个测试宏,有任何一个测试失败,整个功能单元就算失败了,否则就算成功。
TEST(TestSuiteName, TestName) {
... 任意C++代码 ...
}
TEST() 的第一个参数是整个测试单元的名字,第二个参数是测试单元内部的名字。两个名字都必须是合法的C++标识符,不应该包含下划线"_"。在稍后的输出信息中,该单元的输出信息都以 "TestSuiteName.TestName" 标识。
例如,我们定义一个整型函数:
int Factorial(int n); // 返回 n 的阶乘
对应的测试单元可以按照下面这样写:
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(Factorial(0), 1);
}
// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(Factorial(1), 1);
EXPECT_EQ(Factorial(2), 2);
EXPECT_EQ(Factorial(3), 6);
EXPECT_EQ(Factorial(8), 40320);
}
观察两个 TEST() 的参数,应明白它们都属于同一个测试单元(FactorialTest),只不过一个是 HandlesZeroInput 测试,一个是 HandlePositiveInput 测试。这两个名字可以随意取,但是最好贴合测试的实际功能。
TEST_F(Test Fixtures):使用相同的数据配置进行多重测试
如果我们发现自己在使用同样的数据进行多种测试,为了方便,就可以使用 TEST_F()。当然,坚持使用 TEST() 也是可以的,只不过在这种情况下可能要写大量的重复代码。使用
https://github.com/google/googletest/blob/master/googletest/docs/primer.md