我要努力工作,加油!

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")

添加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指向的内容不同,忽略大小写

简单实例

创建一个测试:

  1. 使用 TEST() 宏定义和命名一个测试单元,这个过程很像定义没有返回值的函数。
  2. 在 TEST() 功能区内可以编写任意的合法C++代码,使用前面讨论的各种测试宏测试功能。
  3. 测试结果取决于 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