Gtest:参数化
转自:玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化
一、前言
在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的,程序员都懒,都希望能够少写代码,多复用代码。Google的程序员也一样,他们考虑到了这个问题,并且提供了一个灵活的参数化测试的方案。
二、旧的方案
为了对比,我还是把旧的方案提一下。首先我先把被测函数IsPrime帖过来(在gtest的example1.cc中),这个函数是用来判断传入的数值是否为质数的。
// Returns true iff n is a prime number.
bool IsPrime(int n)
{
// Trivial case 1: small numbers
if (n <= 1) return false; // Trivial case 2: even numbers
if (n % 2 == 0) return n == 2; // Now, we have that n is odd and n >= 3. // Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2) {
// We only have to try i up to the squre root of n
if (i > n/i) break; // Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0) return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
假如我要编写判断结果为True的测试案例,我需要传入一系列数值让函数IsPrime去判断是否为True(当然,即使传入再多值也无法确保函数正确,呵呵),因此我需要这样编写如下的测试案例:
TEST(IsPrimeTest, HandleTrueReturn)
{
EXPECT_TRUE(IsPrime(3));
EXPECT_TRUE(IsPrime(5));
EXPECT_TRUE(IsPrime(11));
EXPECT_TRUE(IsPrime(23));
EXPECT_TRUE(IsPrime(17));
}
我们注意到,在这个测试案例中,我至少复制粘贴了4次,假如参数有50个,100个,怎么办?同时,上面的写法产生的是1个测试案例,里面有5个检查点,假如我要把5个检查变成5个单独的案例,将会更加累人。
接下来,就来看看gtest是如何为我们解决这些问题的。
三、使用参数化后的方案
1. 告诉gtest你的参数类型是什么
你必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型,比如上面的例子,我需要参数化一个int型的参数
class IsPrimeParamTest : public::testing::TestWithParam<int>
{ };
2. 告诉gtest你拿到参数的值后,具体做些什么样的测试
这里,我们要使用一个新的宏(嗯,挺兴奋的):TEST_P,关于这个"P"的含义,Google给出的答案非常幽默,就是说你可以理解为”parameterized" 或者 "pattern"。我更倾向于 ”parameterized"的解释,呵呵。在TEST_P宏里,使用GetParam()获取当前的参数的具体值。
TEST_P(IsPrimeParamTest, HandleTrueReturn)
{
int n = GetParam();
EXPECT_TRUE(IsPrime(n));
}
嗯,非常的简洁!
3. 告诉gtest你想要测试的参数范围是什么
使用INSTANTIATE_TEST_CASE_P这宏来告诉gtest你要测试的参数范围:
INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
第一个参数是测试案例的前缀,可以任意取。
第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:IsPrimeParamTest
第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数。Google提供了一系列的参数生成的函数:
| 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)作为一个参数。 说明:这个功能只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1。 |
四、参数化后的测试案例名
因为使用了参数化的方式执行案例,我非常想知道运行案例时,每个案例名称是如何命名的。我执行了上面的代码,输出如下:

从上面的框框中的案例名称大概能够看出案例的命名规则,对于需要了解每个案例的名称的我来说,这非常重要。 命名规则大概为:
prefix/test_case_name.test.name/index
五、类型参数化
gtest还提供了应付各种不同类型的数据时的方案,以及参数化类型的方案。我个人感觉这个方案有些复杂。首先要了解一下类型化测试,就用gtest里的例子了。
首先定义一个模版类,继承testing::Test:
template <typename T>
class FooTest : public testing::Test {
public:
typedef std::list<T> List;
static T shared_;
T value_;
};
接着我们定义需要测试到的具体数据类型,比如下面定义了需要测试char,int和unsigned int :
typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes);
又是一个新的宏,来完成我们的测试案例,在声明模版的数据类型时,使用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);
}
上面的例子看上去也像是类型的参数化,但是还不够灵活,因为需要事先知道类型的列表。gtest还提供一种更加灵活的类型参数化的方式,允许你在完成测试的逻辑代码之后再去考虑需要参数化的类型列表,并且还可以重复的使用这个类型列表。下面也是官方的例子:
template <typename T>
class FooTest : public testing::Test {
}; TYPED_TEST_CASE_P(FooTest);
接着又是一个新的宏TYPED_TEST_P类完成我们的测试案例:
TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0;
}
TYPED_TEST_P(FooTest, HasPropertyA) { }
接着,我们需要我们上面的案例,使用REGISTER_TYPED_TEST_CASE_P宏,第一个参数是testcase的名称,后面的参数是test的名称
REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);
接着指定需要的类型列表:
typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
这种方案相比之前的方案提供更加好的灵活度,当然,框架越灵活,复杂度也会随之增加。
六、总结
gtest为我们提供的参数化测试的功能给我们的测试带来了极大的方便,使得我们可以写更少更优美的代码,完成多种参数类型的测试案例。
Gtest:参数化的更多相关文章
- gtest 参数化
前言: 在测试用例中,我们时常需要传给被测函数不同的值,gtest为我们提供了简便的方法,可以使我们能够灵活的进行参数化测试. 步骤: 1.创建一个类,继承testing::TestWithParam ...
- 玩转Google开源C++单元测试框架Google Test系列(gtest)(转)
转自:http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Googl ...
- 玩转Google开源C++单元测试框架Google Test系列(gtest)(总)
原文地址:http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Goo ...
- 推荐:一个写的相当好的介绍C++单元测试框架Google Test (gtest) 教程
原文来自:http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 虽然有点晚了,还是一口气读完了全部文章.作者言简意赅和明快的风格 ...
- [转]玩转Google开源C++单元测试框架Google Test系列(gtest)(总)
文章转载自CoderZh的技术博客 地址:https://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Goog ...
- [转]玩转Google开源C++单元测试框架Google Test系列
gtest的官方网站是: http://code.google.com/p/googletest/ 从官方的使用文档里,你几乎可以获得你想要的所有东西 http://code.google.com/p ...
- 转:玩转Google开源C++单元测试框架Google Test系列
转自http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Google ...
- 玩转Google开源C++单元测试框架Google Test系列(转载)
越来越多公司采用敏捷开发,单元和回归测试越来越重要,GTest作为最佳C++单元测试工具越来越多的被使用.转自 http://www.cnblogs.com/coderzh/archive/2009/ ...
- Google的开源C++单元测试框架Google Test
玩转Google开源C++单元测试框架Google Test系列(gtest)(总) 前段时间学习和了解了下Google的开源C++单元测试框架Google Test,简称gtest,非常的不错. 我 ...
随机推荐
- Egret自定义计时器(TimerManager和Laya.timer)
一 自定义计时器 因为游戏中经常用到计时器,比如每1秒发射一枚子弹啊,每2秒怪物AI自动转向啊 每次去new Timer 然后addEventListener(egret.TimerEvent... ...
- 多用户远程连接设置(WindowsServer2008/Win7)
一.Windows server2008 1.点击计算机--->右键属性打开系统对话框.进行如图设置. 2.在开始菜单--->运行中输入gpedit.msc打开本地组策略编辑器对话框. 3 ...
- Grammar01 语法七要素之一_词类
1 词类 1.1 词类表格 实词 名词 -> n. -> noun -> 给所有人和物命名的词. 动词 -> v. (vt. vi.) -> verb ( transit ...
- 将博客转成pdf
前些天无意间看到了“birdben”的博客,写的比较详细,但是最新的文章更新时间是“2017-05-07”,时间很是久远,本打算有时间认真学习一下博主所写的文章,但是担心网站会因为某些原因停止服务,于 ...
- 大数据分析常用去重算法分析『Bitmap 篇』
大数据分析常用去重算法分析『Bitmap 篇』 mp.weixin.qq.com 去重分析在企业日常分析中的使用频率非常高,如何在大数据场景下快速地进行去重分析一直是一大难点.在近期的 Apache ...
- IDEA和JIRA任务联动(redmine同理)
IDEA和JIRA任务联动-操作流程详解 redmine和这个步骤类似,只是第一步选择的工具不一样 操作流程 添加jira配置,File->Setting-->tools-->Tas ...
- 2019 C语言测试
求一元二次方程ax² + bx + c = 0的解 a,b,c为任意整数.(10分) 编写一个口令输入程序,让用户不停输入口令,直到输对为止,假设口令为456.(8分) 输出1000-9999之间能 ...
- 嵌入式02 STM32 实验08 外部中断
一.中断 由于某个事件的发生,CPU暂停当前正在执行的程序,转而执行处理事件的一个程序.该程序执行完成后,CPU接着执行被暂停的程序.这个过程称为中断.(我正在捉泥鳅,但是我妈喊我回家吃饭,我必须回家 ...
- Connection to 天mysql failed. [08001] Could not create connection to database server. Attempted ,报错处理方法
https://blog.csdn.net/myzh215219/article/details/90314345 点击图上的DRIVER,然后点击GO TO DRIVER,之后更改合适的驱动. 我的 ...
- 词汇AFFRIKE非洲AFFRIKE英语
affrike 英文单词,含义是非洲,非洲大陆. 中文名:非洲 外文名:affrike 目录 释义 affrike noun名词 非洲,也用做africa 1.Word Origin and Hist ...