Google单元测试框架gtest--值参数测试
测试一个方法,需要较多个参数进行测试,比如最大值、最小值、异常值和正常值。这中间会有较多重复代码工作,而值参数测试就是避免这种重复性工作,并且不会损失测试的便利性和准确性。
如果测试一个函数,需要些各种参数进行边界测试,比如测试是否为素数,需要测试各种参数。
方法1:
class Prime {
public:
bool IsPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i * i <= n; i++) {
// n is divisible by an integer other than 1 and itself.
if ((n % i) == 0) return false;
}
return true;
}
};
TEST(IsPrimeTest, Negative) {
Prime prime;
EXPECT_FALSE(prime.IsPrime(-5));
EXPECT_FALSE(prime.IsPrime(-1));
}
TEST(IsPrimeTest, Trivial) {
Prime prime;
EXPECT_FALSE(prime.IsPrime(0));
EXPECT_FALSE(prime.IsPrime(1));
EXPECT_TRUE(prime.IsPrime(2));
EXPECT_TRUE(prime.IsPrime(3));
}
输出结果为:

上述测试并不为完整,但这其中就出现了很多重复的代码,比如每个testcasee都需要创建prime和写EXPECT语句。
方法2:使用testfixture,可以减少创建prime类的操作。上面的测试创建prime就是待测数据初始化,如果准备初始化环境复杂,使用test fixtrue可以极大提高效率且保证每个test的运行条件一样。
class PrimeTest : public ::testing::Test {
protected:
Prime prime;
};
TEST_F(PrimeTest, Negative) {
EXPECT_FALSE(prime.IsPrime(-5));
EXPECT_FALSE(prime.IsPrime(-1));
}
TEST_F(PrimeTest, Trivial) {
EXPECT_FALSE(prime.IsPrime(0));
EXPECT_FALSE(prime.IsPrime(1));
EXPECT_TRUE(prime.IsPrime(2));
EXPECT_TRUE(prime.IsPrime(3));
}
输出结果为:

方法2比方法1改进很多,但是依然没有改变代码重复的问题,继续改进,使用循环。
方法3:使用循环消除重复代码。
class PrimeTest : public ::testing::Test {
protected:
Prime prime;
};
TEST_F(PrimeTest, Negative) {
auto vec = std::vector<int>{-5, -1, 0, 1};
for (auto v : vec) {
EXPECT_FALSE(prime.IsPrime(v));
}
}
TEST_F(PrimeTest, Trivial) {
auto vec2 = std::vector<int>{2, 3, 5, 7};
for_each(vec2.begin(), vec2.end(), [&](int a) {
EXPECT_TRUE(prime.IsPrime(a));
}
);
}
输出结果为:

方法3消除了复制代码语句,可以完成同类型参数的测试,但是有一个大问题,很多参数公用一个测试用例,如果某个参数出错,代码不能指示出是哪个test失败了。
// 例如第一个用例有个参数写错了
TEST_F(PrimeTest, Negative) {
auto vec = std::vector<int>{-5, -1, 0, 2};
for (auto v : vec) {
EXPECT_FALSE(prime.IsPrime(v));
}
}
代码出结果是第一个test出错,只能提示到 PrimeTest.Negative失败了。倘若参数列表有很多参数,那么就不容易排查哪里失败了,这违背了测试的基本原则。
[ RUN ] PrimeTest.Negative
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(297): error: Value of: prime.IsPrime(v)
Actual: true
Expected: false
[ FAILED ] PrimeTest.Negative (1 ms)
比如方法1和方法2中的测试,同样的错误可以给出如下详细的错误提示。
[ RUN ] PrimeTest.Negative
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(277): error: Value of: prime.IsPrime(2)
Actual: true
Expected: false
[ FAILED ] PrimeTest.Negative (1 ms)
方法4:幸好gtest给出了解决方案,既能避免重复代码,又能每个测试单独运行每个参数的测试用例,出错后能准确的报告错误的位置。
class PrimeTest : public ::testing::TestWithParam<int> {
protected:
Prime prime;
};
TEST_P(PrimeTest, ReturnsFalseForNonPrimes) {
int n = GetParam();
EXPECT_FALSE(this->prime.IsPrime(n));
}
INSTANTIATE_TEST_CASE_P(myParmTest, // Instance name
PrimeTest, // Test case name
testing::Values(-5,0,1,4)); // Type list
第一,PrimeTest继承于TestWithParm<int>; 相当于创建了一个test suite。
第二,使用Test_P创建test case,第一个参数是test fixture类名,第二个参数test case 名。在测试case里,可以使用GetParam获取每个参数,使用this指针使用Prime类实例。
第三,注册测试case,INSTANTIATE_TEST_CASE_P 宏第一个参数是测试的名字,第二个参数是测试fixtue名字或test case名字。第三个参数是需要输入到case里运行的参数列表,使用values接收列表数据。
输出结果如下图,values(-5,0,1,4)合计4个参数,运行4个tests。

对比方法3,如果某个数据测试出错,可以准备报告错误信息。比如testing::Values(-5,0,1,5)),最后一个参数写成5,输出如下。提示最后一个用例test3,参数为5运行失败.
myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = 5 (1 ms)
[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from myParmTest/PrimeTest
[ RUN ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/0
[ OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/0 (0 ms)
[ RUN ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/1
[ OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/1 (0 ms)
[ RUN ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/2
[ OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/2 (0 ms)
[ RUN ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(320): error: Value of: this->prime.IsPrime(n)
Actual: true
Expected: false
[ FAILED ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = 5 (1 ms)
方法5:上面方法4有个明显缺点,test::Values列表的参数只能是符合test case条件的数,即全是测试false的数,如果需要一些素数测试结果也是true的测试,那么就需要再写个test case,这显然不是很好的设计,那么可以考虑把测试的结果true或false也当做参数传递个测试用例,这样就可以在一个test case里实现素数和非素数的测试工作。具体实现也很简单,TestWithParam<T>,当T是一个组合数时,就实现了上述目标。
class PrimeTest : public ::testing::TestWithParam<std::pair<int, bool>>{
protected:
Prime prime;
};
TEST_P(PrimeTest, ReturnsFalseForNonPrimes) {
auto parm = GetParam();
ASSERT_EQ(this->prime.IsPrime(parm.first), parm.second);
}
INSTANTIATE_TEST_CASE_P(myParmTest,
PrimeTest,
testing::Values(std::make_pair(-5, false),
std::make_pair(-5, false),
std::make_pair(0, false),
std::make_pair(1, false),
std::make_pair(4, false),
std::make_pair(2, true),
std::make_pair(3, true),
std::make_pair(5, true)
));
输出结果如下,8个测试用例,5个false和3个true的测试:

如果某个参数测试失败,可以清晰的输出测试错误信息。
[ RUN ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(342): error: Expected equality of these values:
this->prime.IsPrime(parm.first)
Which is: true
parm.second
Which is: false
[ FAILED ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = (11, false) (1 ms)

尊重技术文章,转载请注明!
Google单元测试框架gtest之官方sample笔记2--类型参数测试
Google单元测试框架gtest--值参数测试的更多相关文章
- Google单元测试框架gtest之官方sample笔记2--类型参数测试
gtest 提供了类型参数化测试方案,可以测试不同类型的数据接口,比如模板测试.可以定义参数类型列表,按照列表定义的类型,每个测试case都执行一遍. 本例中,定义了2种计算素数的类,一个是实时计算, ...
- Google单元测试框架gtest之官方sample笔记3--值参数化测试
1.7 sample7--接口测试 值参数不限定类型,也可以是类的引用,这就可以实现对类接口的测试,一个基类可以有多个继承类,那么可以测试不同的子类功能,但是只需要写一个测试用例,然后使用参数列表实现 ...
- Google单元测试框架gtest之官方sample笔记4--事件监控之内存泄漏测试
sample 10 使用event listener监控Water类的创建和销毁.在Water类中,有一个静态变量allocated,创建一次值加一,销毁一次值减一.为了实现这个功能,重载了new和d ...
- Google单元测试框架gtest之官方sample笔记1--简单用例
1.0 通用部分 和常见的测试工具一样,gtest提供了单体测试常见的工具和组件.比如判断各种类型的值相等,大于,小于等,管理多个测试的测试组如testsuit下辖testcase,为了方便处理初始化 ...
- 简单易懂的单元测试框架-gtest(一)
简介 gtest是google开源的一个单元测试框架,以其简单易学的特点被广泛使用.该框架以第三方库的方式插入被测代码中.同其他单元测试框架相似,gtest也通过制作测试样例来进行代码测试.同 ...
- C++单元测试框架gtest使用
作用 作为代码编码人员,写完代码,不仅要保证编译通过和运行,还要保证逻辑尽量正确.单元测试是对软件可测试最小单元的检查和校验.单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成 ...
- Google C++单元测试框架---Gtest框架简介(译文)
一.设置一个新的测试项目 在用google test写测试项目之前,需要先编译gtest到library库并将测试与其链接.我们为一些流行的构建系统提供了构建文件: msvc/ for Visual ...
- 简单易懂的单元测试框架-gtest(二)
简介 事件机制用于在案例运行前后添加一些操作(相当于挂钩函数).目前,gtest提供了三种等级的事件,分别: 全局级,所有案例执行的前后 TestSuite级,某一个案例集的前后 TestCa ...
- Google C++单元测试框架GoogleTest---GTest的Sample1和编写单元测试的步骤
如果你还没有搭建gtest框架,可以参考我之前的博客:http://www.cnblogs.com/jycboy/p/6001153.html.. 1.The first sample: sample ...
随机推荐
- memcache安装及解决无法远程连接的问题
Memcached是什么 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统. Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fit ...
- BZOJ1150 [CTSC2007]数据备份Backup 链表+小根堆
BZOJ1150 [CTSC2007]数据备份Backup 题意: 给定一个长度为\(n\)的数组,要求选\(k\)个数且两两不相邻,问最小值是多少 题解: 做一个小根堆,把所有值放进去,当选择一个值 ...
- hdu 6863 Isomorphic Strings 哈希+求公因子
题意: t组输入,每组数据输入一个整数n,代表字符串长度.下面再输入一个字符串 你需要判断这个字符串能不能分成大于1段,且这些段的最小表示法是一样的 例如:abccab,它可以分成2段,分别是abc和 ...
- Codeforces Round #673 (Div. 2) C. k-Amazing Numbers (DP,思维)
题意:有一组数,分别用长度从\([1,n]\)的区间去取子数组,要求取到的所有子数组中必须有共同的数,如果满足条件数组共同的数中最小的数,否则输出\(-1\). 题解:我们先从后面确定每两个相同数之间 ...
- HDU - 1789 dp
题意: 众所周知lyb根本不学习.但是期末到了,平时不写作业的他现在有很多作业要做. CUC的老师很严格,每个老师都会给他一个DDL(deadline). 如果lyb在DDL后交作业,老师就会扣他的分 ...
- Codeforces Round #669 (Div. 2) B. Big Vova (枚举)
题意:有一个长度为\(n\)的序列,你需要对其重新排序,构造一个新数组\(c\),\(c_{i}=gcd(a_{1},...,a{i})\)并且使得\(c\)的字典序最小. 题解:直接跑\(n\)次, ...
- Linux-字符处理命令
目录 1.sort(排序) 2.uniq(不相邻的两行重复不会去除) 3.cut(取列,截取字段) 4.wc(统计行.单词.字符数) 1.sort(排序) 选项: -t # 指定分隔符 -k # 指定 ...
- PAT l2-018 多项式A除以多项式B 【多项式+模拟】
这仍然是一道关于A/B的题,只不过A和B都换成了多项式.你需要计算两个多项式相除的商Q和余R,其中R的阶数必须小于B的阶数. 输入格式: 输入分两行,每行给出一个非零多项式,先给出A,再给出B.每行的 ...
- Leetcode(82)-删除排序链表中的重复元素 II
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 示例 1: 输入: 1->2->3->3->4->4->5 输出: 1-&g ...
- Redis内存管理中的LRU算法
在讨论Redis内存管理中的LRU算法之前,先简单说一下LRU算法: LRU算法:即Least Recently Used,表示最近最少使用页面置换算法.是为虚拟页式存储管理服务的,是根据页面调入内存 ...