上一讲介绍了GTest的安装和如何在项目中使用GTest,这一讲主要介绍GTest的断言机制和宏测试
1、断言
一般的,要测试一个方法(函数)是否是正常执行的,可以提供一些输入数据,在调用这个方法(函数)后,得到输出数据,然后检查输出的数据是否与我们期望的结果是一致的,若一致,则说明这个方法的逻辑是正确的,否则,就有问题。 在对输出结果进行检查(check)时,GTest为我提供了一系列的断言(assertion)来进行代码测试,这些宏有点类似于函数调用。
当断言失败时GTest将会打印出assertion时的源文件和出错行的位置,以及附加的失败信息。这些输出的附加信息用户可以直接通过“<<”在这些断言宏后面。 如:
EXPRCT_TRUE(bFlag) << " bFlag is false";
测试宏可以分为两大类:
- ASSERT_*
- EXPECT_*
这些成对的断言功能相同,但效果不同。其中ASSERT_*将会在失败时产生致命错误并中止当前调用它的函数执行(注意不是当前测试用例)。而EXPECT_会生成非致命错误,不会中止当前函数,而是继续执行当前函数。通常情况应该首选使用EXPECT_,因为ASSERT_*在报告完错误后不会进行清理工作,有可能导致内容泄露问题。
1.1 基本断言(真值比较)
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_TRUE(condition); | EXPECT_TRUE(condition); | condition is true |
ASSERT_FALSE(condition); | EXPECT_FALSE(condition); | condition is false |
1.2 二值比较
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
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 |
一般来说二进制比较,都是对比其结构体所在内存的内容。C++大部分原生类型都是可以使用二进制对比的。但是对于自定义类型,我们就要定义一些操作符的行为,比如=、<等。
1.3 字符串比较
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_STREQ(str1,str2); | EXPECT_STREQ(str1,str2); | the two C strings have the same content |
ASSERT_STRNE(str1,str2); | EXPECT_STRNE(str1,str2); | the two C strings have different content |
ASSERT_STRCASEEQ(str1,str2); | EXPECT_STRCASEEQ(str1,str2); | the two C strings have the same content, ignoring case |
ASSERT_STRCASENE(str1,str2); | EXPECT_STRCASENE(str1,str2); | the two C strings have different content, ignoring case |
1.4 浮点对比断言
在对比数据方面,我们往往会讨论到浮点数的对比。因为在一些情况下,浮点数的计算精度将影响对比结果,所以这块都会单独拿出来说。GTest对于浮点数的对比也是单独的
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_FLOAT_EQ(val1, val2); | EXPECT_FLOAT_EQ(val1, val2); | the two float values are almost equal |
ASSERT_DOUBLE_EQ(val1, val2); | EXPECT_DOUBLE_EQ(val1, val2); | the two double values are almost equal |
almost euqal表示两个数只是近似相似,默认的是是指两者的差值在4ULP之内(Units in the Last Place)。我们还可以自己制定精度
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_NEAR(val1, val2, abs_error); | EXPECT_NEAR(val1, val2, abs_error); | the difference between val1 and val2 doesn’t exceed the given absolute error |
使用方法是
ASSERT_NEAR(-1.0f, -1.1f, 0.2f);
ASSERT_NEAR(2.0f, 3.0f, 1.0f);
1.5 成功失败断言
该类断言用于直接标记是否成功或者失败。可以使用SUCCEED()宏标记成功,使用FAIL()宏标记致命错误(同ASSERT_),ADD_FAILURE()宏标记非致命错误(同EXPECT_)。举个例子
if (Check) {
SUCCEED();
}
else {
FAIL();
}
我们直接在自己的判断下设置断言。这儿有个地方需要说一下,SUCCEED()宏会调用GTEST_MESSAGE_AT_宏,从而会影响TestResult的test_part_results结构体,这也是唯一的成功情况下影响该结构体的地方。
1.6 异常断言
异常断言是在断言中接收一定类型的异常,并转换成断言形式。它有如下几种
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_THROW(statement, exception_type); | EXPECT_THROW(statement, exception_type); | statement throws an exception of the given type |
ASSERT_ANY_THROW(statement); | EXPECT_ANY_THROW(statement); | statement throws an exception of any type |
ASSERT_NO_THROW(statement); | EXPECT_NO_THROW(statement); | statement doesn’t throw any exception |
举一个例子
void ThrowException(int n) {
switch (n) {
case 0:
throw 0;
case 1:
throw "const char*";
case 2:
throw 1.1f;
case 3:
return;
}
}
TEST(ThrowException, Check) {
EXPECT_THROW(ThrowException(0), int);
EXPECT_THROW(ThrowException(1), const char*);
ASSERT_ANY_THROW(ThrowException(2));
ASSERT_NO_THROW(ThrowException(3));
}
这组测试特例中,我们预期ThrowException在传入0时,会返回int型异常;传入1时,会返回const char*异常。传入2时,会返回异常,但是异常类型我们并不关心。传入3时,不返回任何异常。当然ThrowExeception的实现也是按以上预期设计的。
1.7 参数名输出断言
在之前的介绍的断言中,如果在出错的情况下,我们会对局部测试相关信息进行输出,但是并不涉及其可能传入的参数。参数名输出断言,可以把参数名和对应的值给输出出来。
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED1(pred1, val1); | EXPECT_PRED1(pred1, val1); | pred1(val1) returns true |
ASSERT_PRED2(pred2, val1, val2); | EXPECT_PRED2(pred2, val1, val2); | pred2(val1, val2) returns true |
目前版本的GTest支持5个参数的版本ASSERT/EXPECT_PRED5宏。其使用方法是:
template <typename T1, typename T2>
bool GreaterThan(T1 x1, T2 x2) {
return x1 > x2;
}
TEST(PredicateAssertionTest, AcceptsTemplateFunction) {
int a = 5;
int b = 6;
ASSERT_PRED2((GreaterThan<int, int>), a, b);
}
其输出是
error: (GreaterThan<int, int>)(a, b) evaluates to false, where
a evaluates to 5
b evaluates to 6
1.8 子过程中使用断言
经过之前的分析,我们可以想到,如果子过程中使用了断言,则结果输出只会指向子过程,而不会指向父过程中的某个调用,如果在父过程中多次调用这个子过程,那么就无法分析是哪一次调用失败。为了便于阅读我们可以使用SCOPED_TRACE宏去标记下位置
void Sub(int n) {
ASSERT_EQ(1, n);
}
TEST(SubTest, Test1) {
{
SCOPED_TRACE("A");
Sub(2);
}
Sub(3);
}
其结果输出时标记了下A这行位置,可见如果没有这个标记,是很难区分出是哪个Sub失败的。
..\test\gtest_unittest.cc(87): error: Expected: 1
To be equal to: n
Which is: 2
Google Test trace:
..\test\gtest_unittest.cc(92): A
..\test\gtest_unittest.cc(87): error: Expected: 1
To be equal to: n
Which is: 3
我们再注意下Sub的实现,其使用了ASSERT_EQ断言,该断言并不会影响Test1测试特例的运行,其原因我们在之前做过分析了。为了消除这种可能存在的误解,GTest推荐使用在子过程中使用
ASSERT/EXPECT_NO_FATAL_FAILURE(statement);
如果父过程一定要在子过程发生错误时退出怎么办?我们可以使用::testing::Te