本文共 3483 字,大约阅读时间需要 11 分钟。
这篇开始学习单元测试示范,在GTest这个下载包,里面有一个samples文件夹,里面有10个单元测试例子,告诉我们怎么去使用googletest这个测试框架。我认为,这种官方文档,是十分有必要认真,一个一个学习完成,你才可以说基本会用GTest这个框架。我们先学习会用,高级的原理层我们等有基础再尝试去看看源码学习学习。
1.第一个单元测试:n的阶乘和n是否素数两个函数的单元测试
一共三个文件,一个头文件,一个实现文件,一个是单元测试文件。典型模拟我们真实项目中的情况。
2.相关代码拷贝到vs2015
我把这三个文件,拷贝到了vs2015环境,然后把注释用中文写了一遍
第一个头文件sample01.h 代码如下
// 使用Google C++ testing 框架的一个简单的编程示例#ifndef GTEST_SAMPLES_SAMPLE01_H_#define GTEST_SAMPLES_SAMPLE01_H_// 返回n的阶乘,如果n为负数,返回1int Factorial(int n);// 如果是素数返回true,否则返回falsebool IsPrime(int n);#endif // GTEST_SAMPLES_SAMPLE01_H_
实现类cpp文件
// 使用Google C++ testing 框架的一个简单的编程示例#include "sample01.h"// 返回n的阶乘,如果n为负数,返回1int Factorial(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result;}// 如果是素数返回true,否则返回falsebool IsPrime(int n) { // 分支1: n小于等于1的数 if (n <= 1) return false; // 分支2: 偶数 if (n % 2 == 0) return n == 2; // 接下来n为奇数,且n>=3 // i从3开始,尝试用n去除奇数i for (int i = 3; ; i += 2) { // 我们只需要尝试i直到n的平方根 if (i > n / i) break; // 现在 i <= n/i < n. // 如果n能被i整除,n不是素数 if (n % i == 0) return false; } // n 在范围 (1, n)没有整数因子,所以n是素数 return true;}
上面两个函数,第一个求n的阶乘。第二个是判断一个数是不是素数。具体什么是素数可以先百度一下,这种函数内部分支覆盖的白盒测试是需要完整理解函数内部的逻辑。
单元测试类代码
// 使用 1-2-3步骤,很容易使用Gtest框架编写单元测试// 步骤1. 根据你的实际情况,包含所需的头文件// 不要忘记包含 gtest.h, 采用使用GTest测试框架#include#include "sample01.h"#include "gtest/gtest.h"namespace { // 步骤2. 使用TEST宏去定义你的测试 // // TEST 宏有两个参数: 测试用例名称 和测试名称. // 使用TEST宏之后, 你需要在{}之内定义你的测试逻辑。你可以使用一堆宏来断言成功还是失败. EXPECT_TRUE 和 EXPECT_EQ // 是这堆断言宏的两个常用的宏. 可以打开gtest.h阅读找到全部的断言宏。 // //在GTest中,测试被划分为不同的测试用例. 这样组织用例,保证了测试代码的井井有条。 //你应该把逻辑相关的测试放入奥相同的测试用例中。 // // 测试名称和测试用例名称都需要符合C++标识符规范 // 并且在测试名称和测试用例名称中,你不能使用下划线(_) // Google Test 框架可以确保每个测试都执行一次,但不确保测试顺序执行。 //所以,你要确保你写的测试的结果不依赖其他测试的顺序 // 测试函数 Factorial(). // 使用负数测试斐波那契数列 TEST(FactorialTest, Negative) { // This test is named "Negative", and belongs to the "FactorialTest" // 测试用例 EXPECT_EQ(1, Factorial(-5)); EXPECT_EQ(1, Factorial(-1)); EXPECT_GT(Factorial(-10), 0); // EXPECT_EQ(expected, actual) 等价于 EXPECT_TRUE((expected) == (actual)) // 当断言发生失败,EXPECT 相关宏会同时打印实际结果和预期结果 // 这在调试的时候非常有用 // 因此在这种情况下首选 EXPECT_EQ宏用来断言 // // 另一边,EXPECT_TRUE 宏接受一切的布尔表达式,更为笼统。 } // 测试0的阶乘 TEST(FactorialTest, Zero) { EXPECT_EQ(1, Factorial(0)); } // 测试正整数的阶乘 TEST(FactorialTest, Positive) { EXPECT_EQ(1, Factorial(1)); EXPECT_EQ(2, Factorial(2)); EXPECT_EQ(6, Factorial(3)); EXPECT_EQ(40320, Factorial(8)); } // 以下测试函数 IsPrime() // 测试负数输出 TEST(IsPrimeTest, Negative) { // 这个测试点属于 IsPrimeTest测试用例 EXPECT_FALSE(IsPrime(-1)); EXPECT_FALSE(IsPrime(-2)); EXPECT_FALSE(IsPrime(INT_MIN)); } // 测试一些简单的数字n=0,1,2,3 TEST(IsPrimeTest, Trivial) { EXPECT_FALSE(IsPrime(0)); EXPECT_FALSE(IsPrime(1)); EXPECT_TRUE(IsPrime(2)); EXPECT_TRUE(IsPrime(3)); } // 测试一些正整数n TEST(IsPrimeTest, Positive) { EXPECT_FALSE(IsPrime(4)); EXPECT_TRUE(IsPrime(5)); EXPECT_FALSE(IsPrime(6)); EXPECT_TRUE(IsPrime(23)); }} // namespace // 步骤3. 在 main()调用 RUN_ALL_TESTS() . // 这个我们写在了LearnGtest.cpp文件中的main函数
保存之后,点击 生成->生成解决方案,然后 调试->开始执行(不调试),观察测试结果输出报告
上面具体素数的逻辑,需要好好想以下。
3.总结
3.1 上面说的编写Gtest用例采用 1-2-3 三步法,其实就两部:
步骤1:引入头文件,包括被测试对象头文件和gtest.h这个头文件
步骤2:使用TEST宏,有两个参数,第一个是测试用例名称,第二个是测试点名称
文档中说得步骤3是我们在别的文件的main()函数中调用了RUN_ALL_TESTS()方法。
3.2 Gtest中提供了一堆断言用的宏,可以在gtest.h文件找到更多宏
3.3 不需要我们手动把用例注册到Gtest框架,这个框架会自动发现TEST宏的测试用例。
3.4 每个TEST宏的测试会被执行,但是执行不确保用例的顺序
3.5 当前在断言,建议使用EXPECT开头的宏,因为断言出错之后,会打印实际结果和预期结果,方便调试问题
4.其他练习
可以修改断言语句中一个值,让其中一条用例执行失败,看看控制台打印失败的信息能不能根据失败日志,找到定位出问题的代码行,并修复,使测试成功运行通过。
转载地址:http://wrxws.baihongyu.com/