1. 前言
前面一篇【gtest保姆级教程】中我们讲解了一些gtest的入门知识,gtest除了TEST_F
和TEST
外,还有更加高级的用法,使用这些高级用法,会让你的测试效率极大提高,往往一小段测试代码就可以生成出大量的测试集,让你的测试效率事半功倍
2. 进阶用法
2.1 Skip
GTEST_SKIP()
支持忽略当前测试,不管在GTEST_SKIP()
之后是不是断言失败,程序都会在遇到skip的时候跳出当前测试
TEST(SkipTest, DoesSkip) {
GTEST_SKIP() << "Skipping single test";
FAIL(); // Won't fail; it won't be executed
}
2.2 Death Test
我们可能有时候会写一些死测试,gtest提供了死测试断言功能,如下所示:
TEST(MyDeathTest, Foo) {
// This death test uses a compound statement.
ASSERT_DEATH({
int n = 5;
Foo(&n);
}, "Error on line .* of Foo()");
}
TEST(MyDeathTest, NormalExit) {
EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
}
TEST(MyDeathTest, KillProcess) {
EXPECT_EXIT(KillProcess(), testing::KilledBySignal(SIGKILL),
"Sending myself unblockable signal");
}
- 在调用Foo(5)时,程序遇到了一个死测试,此时通过
ASSERT_DEATH
来退出程序,并且打印一些日志 - 调用NormalExit()时,会打印
Success
,并且正常退出,返回值为0 - 调用KillProcess()时,会打印
Sending myself unblockable signal
,并且发送SIGKILL
信号量
注意:
死测试的命名的test suite必须是*DeathTest, 在fixture中,为了保证正常的测试集和死测试集,我们可以使用using或者typedef来定义一个别名,例如
class FooTest : public testing::Test { ... };
using FooDeathTest = FooTest;
TEST_F(FooTest, DoesThis) {
// normal test
}
TEST_F(FooDeathTest, DoesThat) {
// death test
}
2.3 参数化测试
gtest支持使用INSTANTIATE_TEST_SUITE_P()
来生成测试参数,参数的type通过testing::TestWithParam<ParamType>
的模板传进去,当使用参数化时,需要使用TEST_P
来进行测试,例如:
class MyTestSuite : public testing::TestWithParam<int> {};
TEST_P(MyTestSuite, MyTest)
{
std::cout << "Example Test Param: " << GetParam() << std::endl;
}
INSTANTIATE_TEST_SUITE_P(MyGroup, MyTestSuite, testing::Range(0, 10),
testing::PrintToStringParamName());
上面代码中GetParam()
将获取testing::Range(0, 10)
生成的[0, 10)的数据,testing::PrintToStringParamName()
表示会在test suit中通过参数生成一个测试前缀,默认可以不加。
testing::Range
表示生成一个序列,它有三个参数(start,end, step),start和end表示序列的开始与结束,step表示增加的步幅,默认step是1
gtest还提供了testing::Values
来指定数值, 另外还提供了testing::Combine
进行各种参数的组合成一个tuple
, 例如:
enum class MyType { MY_FOO = 0, MY_BAR = 1 };
class MyTestSuite : public testing::TestWithParam<std::tuple<MyType, std::string>> {
};
INSTANTIATE_TEST_SUITE_P(
MyGroup, MyTestSuite,
testing::Combine(
testing::Values(MyType::MY_FOO, MyType::MY_BAR),
testing::Values("A", "B")));
TEST_P(MyTestSuite, MyTest) {
auto param = GetParam();
std::cout << "Example Test Param: " << (int)std::get<0>(param) << ","
<< std::get<1>(param) << std::endl;
}
组合后的四种运行结果如下:
[==========] Running 4 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 4 tests from MyGroup/MyTestSuite
[ RUN ] MyGroup/MyTestSuite.MyTest/0
Example Test Param: 0,A
[ OK ] MyGroup/MyTestSuite.MyTest/0 (0 ms)
[ RUN ] MyGroup/MyTestSuite.MyTest/1
Example Test Param: 0,B
[ OK ] MyGroup/MyTestSuite.MyTest/1 (0 ms)
[ RUN ] MyGroup/MyTestSuite.MyTest/2
Example Test Param: 1,A
[ OK ] MyGroup/MyTestSuite.MyTest/2 (0 ms)
[ RUN ] MyGroup/MyTestSuite.MyTest/3
Example Test Param: 1,B
[ OK ] MyGroup/MyTestSuite.MyTest/3 (0 ms)
[----------] 4 tests from MyGroup/MyTestSuite (0 ms total)
[----------] Global test environment tear-down
[==========] 4 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 4 tests.
2.4 Typed Tests
前面我们提到的TEST
和TEST_F
都是一些逻辑性的测试,有时候我们可能需要对参数的类型进行测试,这时候我们就需要Typed Tests
功能, Typed Tests
需要使用TYPED_TEST
来进行测试,例如:
template <typename T>
class FooTest : public testing::Test {
public:
...
using List = std::list<T>;
static T shared_;
T value_;
};
我们要测试char
, int
, unsigned int
三种type,则使用TYPED_TEST_SUITE
来进行注册
using MyTypes = ::testing::Types<char, int, unsigned int>;
TYPED_TEST_SUITE(FooTest, MyTypes);
然后在TYPED_TEST
中,通过TypeParam
来获取
TYPED_TEST(FooTest, DoesBlah) {
// Inside a test, refer to the special name TypeParam to get the type
// parameter. Since we are inside a derived class template, C++ requires
// us to visit the members of FooTest via 'this'.
TypeParam n = this->value_;
// To visit static members of the fixture, add the 'TestFixture::'
// prefix.
n += TestFixture::shared_;
// To refer to typedefs in the fixture, add the 'typename TestFixture::'
// prefix. The 'typename' is required to satisfy the compiler.
typename TestFixture::List values;
values.push_back(n);
...
}
TYPED_TEST(FooTest, HasPropertyA) { ... }