0. 引言
Google Test是常用的一个开源测试框架,它的框架在测试过程中更加灵活,便捷,在后续演示实例的过程中会提到。源码可以参照https://github.com/google/googletest.git,本篇文章中介绍gtest的基本使用
1. TEST宏
TEST宏是一个很重要的宏,它构成一个测试特例。TEST宏的第一个参数是“测试用例名”,第二个参数是“测试特例名”。测试用例(Test Case)是为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求,测试特例是测试用例下的一组测试。
以下面代码为例,下面两个TEST宏构成的是一个测试用例——测试用例名是IsOdd(奇数方法检测,测试IsOdd函数),包含两组测试用例
#include <gtest/gtest.h>
//判断奇数还是偶数
bool IsOdd(int x)
{
return x % 2 == 1;
}
TEST(IsOddTest,odd1)
{
ASSERT_TRUE(IsOdd(3));
ASSERT_TRUE(IsOdd(5));
ASSERT_TRUE(IsOdd(7));
ASSERT_TRUE(IsOdd(9));
ASSERT_TRUE(IsOdd(11));
}
TEST(IsOddTest,odd2)
{
ASSERT_FALSE(IsOdd(2));
ASSERT_FALSE(IsOdd(4));
ASSERT_FALSE(IsOdd(6));
ASSERT_FALSE(IsOdd(8));
ASSERT_FALSE(IsOdd(10));
}
结果:
[root@VM_0_14_centos 1]# ./test
Running main() from gtest_main.cc
[==========] Running 2 tests from 1 test case.//两组测试来源于一个测试用例
[----------] Global test environment set-up.
[----------] 2 tests from IsOddTest
[ RUN ] IsOddTest.odd1
[ OK ] IsOddTest.odd1 (0 ms)//第一组
[ RUN ] IsOddTest.odd2
[ OK ] IsOddTest.odd2 (0 ms)//第二组
[----------] 2 tests from IsOddTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[ PASSED ] 2 tests.
2. 参数化设计
还是上面判断奇数和偶数的例子;向上面写法自己使用宏来写,有时候测试用例的参数过多,这样写起来很麻烦;gtest里面提供了一个基类testing::TestWithParam< T >,使用者可以继承基类,通过TEST_ P来获取到参数,并且使用INSTANTIATE_TEST_CASE_P 来生成测试用例
class IsOddTestTrueReturn : public testing::TestWithParam<int>//一个子类继承了gtest里面的类
{};
//P是参数化的意思
TEST_P(IsOddTestTrueReturn,TrueReturn){//第一个是类名,第二个随便写
int n = GetParam();//获取参数
EXPECT_TRUE(IsOdd(n));//将参数传入函数里面,进行测试
}
//生成测试用例
INSTANTIATE_TEST_CASE_P(IsOdd,IsOddTestTrueReturn,testing::Values(1,3,5,7,9,11));
class IsOddTestFalseReturn : public testing::TestWithParam<int>
{};
TEST_P(IsOddTestFalseReturn, FalseReturn){
int n = GetParam();
EXPECT_FALSE(IsOdd(n));
}
INSTANTIATE_TEST_CASE_P(IsOdd,IsOddTestFalseReturn,testing::Values(2,4,6,8,10));
结果:
[root@VM_0_14_centos 1]# ./test
Running main() from gtest_main.cc
[==========] Running 11 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 6 tests from IsOdd/IsOddTestTrueReturn//6个测试用例来源于类IsOddTestTrueReturn
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/0
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/0 (0 ms)
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/1
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/1 (0 ms)
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/2
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/2 (0 ms)
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/3
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/3 (0 ms)
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/4
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/4 (0 ms)
[ RUN ] IsOdd/IsOddTestTrueReturn.TrueReturn/5
[ OK ] IsOdd/IsOddTestTrueReturn.TrueReturn/5 (0 ms)
[----------] 6 tests from IsOdd/IsOddTestTrueReturn (0 ms total)
[----------] 5 tests from IsOdd/IsOddTestFalseReturn//6个测试用例来源于IsOddTestFalseReturn
[ RUN ] IsOdd/IsOddTestFalseReturn.FalseReturn/0
[ OK ] IsOdd/IsOddTestFalseReturn.FalseReturn/0 (0 ms)
[ RUN ] IsOdd/IsOddTestFalseReturn.FalseReturn/1
[ OK ] IsOdd/IsOddTestFalseReturn.FalseReturn/1 (0 ms)
[ RUN ] IsOdd/IsOddTestFalseReturn.FalseReturn/2
[ OK ] IsOdd/IsOddTestFalseReturn.FalseReturn/2 (0 ms)
[ RUN ] IsOdd/IsOddTestFalseReturn.FalseReturn/3
[ OK ] IsOdd/IsOddTestFalseReturn.FalseReturn/3 (0 ms)
[ RUN ] IsOdd/IsOddTestFalseReturn.FalseReturn/4
[ OK ] IsOdd/IsOddTestFalseReturn.FalseReturn/4 (0 ms)
[----------] 5 tests from IsOdd/IsOddTestFalseReturn (0 ms total)
[----------] Global test environment tear-down
[==========] 11 tests from 2 test cases ran. (0 ms total)
[ PASSED ] 11 tests.
分析:
从输出结果上,我们看到GTest框架将我们相同测试用例名的场景合并在一起,不同测试特例名的场景分开展现。
虽然上例中,所有的执行都是正确的,但是如果以上测试中发生一个错误,也不能影响其他测试——不同测试用例不相互影响、相同测试用例不同测试特例不相互影响。我们称之为独立性。除了独立性,也不失灵活性——一个测试用例中可以通过不同宏(ASSERT_*类宏会影响之后执行,EXPECT_*类宏不会)控制是否影响之后的执行。
3. 预处理
上述的测试用例是获取单个参数来进行测试,在实际中,我们的函数中往往需要多个参数,在每次测试时都构造一次测试用例,这比较麻烦,因此gtest为我们提供了一种提前构建数据的方式:
int Add(int x,int y)
{
return x+y;
}
struct Param{
int _x;
int _y;
int _sum;
Param(int x = 0,int y = 0, int sum = 0)
:_x(x)
,_y(y)
,_sum(sum)
{}
};
class AddTest : public testing::TestWithParam<Param>
{
public:
virtual void SetUp(){
Param p = GetParam();
std::cout << "case.x:"<< p._x << "case.y" << p._y <<"case.sum:"<< p._sum << std::endl;
}
virtual void TearDown(){
}
Param p;
};
TEST_P(AddTest,test){//真正测试部分,这里将参数传递过去
EXPECT_EQ(Add(p._x,p._y),p._sum);
}
INSTANTIATE_TEST_CASE_P(AddTest,AddTest,testing::Values(Param(1, 1, 2),Param(2, 4, 6),Param(3,4,7)));
TEST_P(AddTest,test1){//真正测试部分,这里将参数传递过去
EXPECT_EQ(Add(p._x,p._y),p._sum);
}
分析:
我们让AddTest继承了testing::TestWithParam< T >(注:testing::TestWithParam< T >继承了testing::Test)并且重载了SetUp方法,这样我们每次执行AddTest的一个测试特例时,SetUp方法都会执行一次,从而将数据准备完毕。这样我们只要在一个类中构建好数据就行了。这儿需要注意一下TEST_P宏,它的第一参数要求是类名——即AddTest——不像TEST宏的第一个参数我们可以随便命名。
注:INSTANTIATE_TEST_CASE_P 这个宏只能为一个测试用例提供参数