1、简介
在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况。按照之前的通用方法,需要多次调用同一个函数,这种做法很不方便,效率也不好。
如:
TEST(IsPrimeTest, HandleTrueReturn)
{
EXPECT_TRUE(IsPrime(3));
EXPECT_TRUE(IsPrime(5));
EXPECT_TRUE(IsPrime(11));
EXPECT_TRUE(IsPrime(23));
EXPECT_TRUE(IsPrime(17));
......
}
这种方法就是1个测试案例,有5个检查点。而且传入再多的值也无法保证函数正确。IsPrime函数在gtest的example1.cc中。因此Google使用参数化的方法解决这种问题。
2、参数化
2.1 告知gtest需要使用的参数类型
必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型。如:参数化int型的参数(放在头文件中)
class myTest : public testing::TestWithParam<int>
{
};
2.2 告知gtest拿到参数后执行的操作当前的参数的具体值
如:
TEST_P(myTest, test0)
{
int num = GetParam();
EXPECT_EQ(num,num)<<"GetParam:"<<GetParam()<<",num:"<<num;
}
此处TEST_P宏中的第一个参数和头文件中定义的参数化的类名相同。
2.3 告知gtest要测试的参数范围
使用INSTANTIATE_TEST_CASE_P宏,如:
INSTANTIATE_TEST_CASE_P(isParamTestInt, myTest, testing::Values(1, 2, 3, 4));
该宏的参数解析:
isParamTestInt:测试案例的前缀,随意取即可
myTest:测试案例名称,要和定义的参数化的类的名称相同
testing::Values(1, 2, 3, 4):参数生成器
该宏放在与TEST_P宏(2.2)相同的文件。
2.4 参数生成函数
Range(begin, end[, step]) 范围在begin~end之间,步长为step,不包括end
Values(v1, v2, ..., vN) v1,v2到vN的值
ValuesIn(container) and ValuesIn(begin, end) 从一个C类型的数组或是STL容器,或是迭代器中取值
Bool() 取false 和 true 两个值
Combine(g1, g2, ..., gN) 将g1,g2,...gN进行排列组合,g1,g2,...gN本身是一个参数生成器,每次分别从g1,g2,..gN中各取出一个值,组合成一个元组(Tuple)作为一个参数。
注意:Combine只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1。
2.5 参数化后的输出
4 tests from isParamTestInt/myTest
[ RUN ] isParamTestInt/myTest.test0/0
[ OK ] isParamTestInt/myTest.test0/0 (0 ms)
[ RUN ] isParamTestInt/myTest.test0/1
[ OK ] isParamTestInt/myTest.test0/1 (0 ms)
[ RUN ] isParamTestInt/myTest.test0/2
[ OK ] isParamTestInt/myTest.test0/2 (0 ms)
[ RUN ] isParamTestInt/myTest.test0/3
[ OK ] isParamTestInt/myTest.test0/3 (0 ms)
[----------] 4 tests from isParamTestInt/myTest (4 ms total)
格式:INSTANTIATE_TEST_CASE_P的第一个参数 / TEST_P第一个参数 . TEST_P第二个参数 / testing::Values中参数的序号,从0开始
3、类型参数化
gtest还提供了应付各种不同类型的数据时的方案,以及参数化类型的方案。
3.1 定义1个模板类,继承testing::Test
template <typename T> class FooTest : public testing::Test {
public:
...
typedef std::list<T> List;
static T shared_;
T value_;
};
3.2 定义需要测试到的具体数据类型
定义需要测试char,int和unsigned int :
typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes);
3.3 使用TYPED_TEST宏完成测试案例,在声明模版的数据类型时,使用TypeParam
TYPED_TEST(FooTest, DoesBlah) {
// 在测试中,引用特殊名称TypeParam以获取类型参数。
// 由于我们在一个派生类模板中,所以C++要求我们通过“this”访问FooTest的成员
TypeParam n = this->value_;
// 访问fixture的静态成员, 添加'TestFixture::'前缀
n += TestFixture::shared_;
// 需要引用fixture中的typedefs, 添加'typename TestFixture::'前缀
typename TestFixture::List values;
values.push_back(n);
}
该示例需要事先知道类型的列表。
gtest还提供一种更加灵活的类型参数化的方式,允许你在完成测试的逻辑代码之后再去考虑需要参数化的类型列表,并且还可以重复的使用这个类型列表。
3.4 官方的另一种方案
3.4.1 定义模板类
template <typename T>
class FooTest : public testing::Test {
};
TYPED_TEST_CASE_P(FooTest);
与3.1 一致,先定义模板类。
3.4.2 使用TYPED_TEST_P宏完成测试案例
TYPED_TEST_P(FooTest, DoesBlah) {
// 测试中,参考TypeParam以获取类型参数
TypeParam n = 0;
}
TYPED_TEST_P(FooTest, HasPropertyA) { }
3.4.3 使用REGISTER_TYPED_TEST_CASE_P宏
REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);
第一个参数是testcase的名称,后面的参数是test的名称
3.4.4 指定需要的类型列表
typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
这种方案相比之前的方案提供更加好的灵活度,当然,框架越灵活,复杂度也会随之增加。