gTest和gMock指引
GTest
安装
-
下载zip
https://github.com/google/googletest/releases -
解压到某个目录
目录树如下
Root ├─.github ├─ci ├─docs ├─googlemock │ ├─cmake │ ├─docs │ ├─include │ ├─src │ └─test └─googletest ├─cmake ├─docs ├─include //包含此目录 ├─samples ├─src └─test -
使用cmake添加以下:
add_subdirectory("Root" "gtest") # 作为子项目附属到文件, 子项目在本目录里的文件名为"gtest" include_directories("Root/googletest/include") # 添加头文件路径 target_link_libraries(ProjectName gtest gtest_main) # 将gtest gtest_main链接到本项目 -
开始使用
#include "gtest/gtest.h" TEST(FooSuiteTest, DISABLED_My_Test) { GTEST_SKIP(); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
测试指引
#include "gtest/gtest.h"
class Foo {
public:
int Bar(int a, int b) {
return a + b;
}
};
// 定义test fixture 必须继承自testing::Test
class FooTest : public ::testing::Test {
protected:
FooTest() {
// 整个测试套件开始前执行
}
~FooTest() override {
// 整个测试套件结束后执行
}
void SetUp() override {
// 套件内的每个测试用例开始前执行
}
void TearDown() override {
// 套件内的每个测试用例结束后执行
}
// 以下可以定义本测试用例共用的成员
};
// 使用测试套件
// TEST_F(test_fixture, 测试用例名)
TEST_F(FooTest, My_Test_F) {
int a = 1;
int b = 1;
Foo f;
// 断言
EXPECT_EQ(f.Bar(a, b), 2);
}
// 直接使用测试用例
// TEST(test_suite_name, 测试用例名)
TEST(FooSuiteTest, My_Test) {
int a = 1;
int b = 1;
Foo f;
// 断言
EXPECT_EQ(f.Bar(a, b), 2);
}
/**************************/
// 自定义Predicate
testing::AssertionResult IsEven(int n) {
if ((n % 2) == 0)
return testing::AssertionSuccess();
else
return testing::AssertionFailure() << n << " is odd";
}
TEST(FooSuiteTest, My_Predicate_Test) {
int a = 1;
int b = 1;
Foo f;
// 断言
EXPECT_TRUE(IsEven(f.Bar(a, b)));
// EXPECT_PRED1(IsEven, f.Bar(a, b)); //同样效果
}
/**************************/
// 跳过测试
TEST(FooSuiteTest, DISABLED_My_Test) {
GTEST_SKIP(); // 跳过本测试用例
}
// 测试异常
TEST(FooSuiteTest, My_Exception_Test) {
int a = 1;
int b = 1;
Foo f;
// 断言
EXPECT_NO_THROW(f.Bar(a, b));
}
// 测试记录, 目的是为了更方便的定位
TEST(FooSuiteTest, My_Trace_Test) {
SCOPED_TRACE("My_Trace_Test"); // 用于跟踪测试用例的执行
}
int main(int argc, char **argv) {
// 解析命令行
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
测试模式:Test_Fixture和Test_Suite
Test_Fixture,是一个用于在多个测试用例之间共享相同状态或者进行初始化和清理操作的类。使用步骤如下:
- 自定义的一个Fixture类,这个类必须继承于
::testing::Test,在这个类中可以定义使用SetUp()和TearDown()方法为每个测试用例进行初始化和清理操作。而这个类的构造函数和析构函数是全部测试开始前的和结束后的工作。在这个类里定义的成员变量/函数会共享实例(而不是为每个测试创建一个实例)。 - 使用TEST_F宏声明测试,第一个参数使用自己定义的Fixture类名,第二个参数是该测试的名称。
Test_Suite,一组相关的测试用例的集合,以便可以对它们进行组合测试。使用步骤如下:
- 使用TEST_F宏声明测试,第一个参数使用自己定义的Suite名,第二个参数是该测试的名称。
Test_Fixture和Test_Suite的区别?
test fixture的作用是减少重复代码,避免在多个测试用例中重复编写初始化和清理代码。而test suite的作用是将相关的测试用例组合在一起,便于管理和执行。而test fixture也具有test suite的功能,可以这样说test fixture是test suite功能上多了初始化和清理代码。
使用断言
gtest的断言主要包括以下几种类型:
EXPECT_EQ(val1, val2)/ASSERT_EQ(val1, val2): 判断两个值是否相等;EXPECT_NE(val1, val2)/ASSERT_NE(val1, val2): 判断两个值是否不相等;EXPECT_TRUE(condition)/ASSERT_TRUE(condition): 判断条件是否为真;EXPECT_FALSE(condition)/ASSERT_FALSE(condition): 判断条件是否为假;EXPECT_LT(val1, val2)/ASSERT_LT(val1, val2): 判断val1是否小于val2;EXPECT_LE(val1, val2)/ASSERT_LE(val1, val2): 判断val1是否小于等于val2;EXPECT_GT(val1, val2)/ASSERT_GT(val1, val2): 判断val1是否大于val2;EXPECT_GE(val1, val2)/ASSERT_GE(val1, val2): 判断val1是否大于等于val2;EXPECT_STREQ(str1, str2)/ASSERT_STREQ(str1, str2): 判断两个C风格字符串是否相等;EXPECT_STRNE(str1, str2)/ASSERT_STRNE(str1, str2): 判断两个C风格字符串是否不相等;EXPECT_THROW(statement, exception_type)/ASSERT_THROW(statement, exception_type): 判断一个异常是否被抛出;EXPECT_NO_THROW(statement)/ASSERT_NO_THROW(statement): 判断一个语句是否没有抛出异常;EXPECT_FLOAT_EQ(val1, val2)/ASSERT_FLOAT_EQ(val1, val2): 判断两个浮点数是否相等;EXPECT_DOUBLE_EQ(val1, val2)/ASSERT_DOUBLE_EQ(val1, val2): 判断两个双精度浮点数是否相等;EXPECT_NEAR(val1, val2, abs_error)/ASSERT_NEAR(val1, val2, abs_error): 判断两个值的差值是否小于某个误差限制。
此外还可以自定义自定义Predicate、记录日志等。见链接Advanced GoogleTest Topics | GoogleTest。
GMock
安装
下载和GTest类似,接下来:
-
使用cmake添加以下:
add_subdirectory("Root" "gtest") include_directories("Root/googletest/include") # 是需要gtest的 include_directories("Root/googlemock/include") target_link_libraries(ProjectName gmock gmock_main) target_link_libraries(ProjectName gtest gtest_main) -
开始使用
测试指引
#include <gmock/gmock.h>
class ICalculator {
public:
virtual ~ICalculator() {}
virtual int Add(int a, int b) = 0;
};
class MockCalculator : public ICalculator {
public:
MOCK_METHOD2(Add, int(int a, int b));
};
TEST(MyTestSuite, MyTestCase) {
MockCalculator mock;
// 设置预期
EXPECT_CALL(mock, Add(3, 5))
.Times(1)
.WillOnce(::testing::Return(8));
EXPECT_EQ(8, mock.Add(3, 5)); // 这将调用预期行为,返回预期的值
}
GMock工作的大致流程
- GMock通过继承需要测试的类,对其中的虚函数使用
MOCK_METHOD宏来“标记”,要求测试的函数是虚函数。 - 使用GTest定义一个测试
- 使用
EXPECT_CALL宏来设置Mock类的行为,此步骤类似函数定义,规定了需要测试的方法的行为,此步骤是Mock的精髓 - 使用断言函数判断是否正确
设置预期行为
EXPECT_CALL(mock, Add(3, 5))
.Times(5)
.WillOnce(Return(100))
.WillOnce(Return(150))
.WillRepeatedly(Return(200)); //Add方法会被调用五次,第一次返回100,第二次返回150,然后每次返回200
gMock提供了许多函数来设置预期行为。以下是一些常用的函数:
-
EXPECT_CALL(obj, Func(args)):设定某个对象obj的某个成员函数Func在特定参数args下应该被调用 -
.Times(n):设定函数应该被调用的次数,n可以是具体数字,也可以是Matcher对象// 测试 Calculator 对象的 Add 函数是否只被调用一次 EXPECT_CALL(obj, Add(_, _)).Times(1); obj.Add(1, 2); -
.WillOnce(action)/.WillRepeatedly(action): 设定该函数应该返回的值或执行的操作,其中 action 可以是 Return(value)、Throw(exception)、Invoke(function) 等//定义当 Calculator 对象的 Add 函数被调用时应该返回什么值 EXPECT_CALL(obj, Add(_, _)).WillOnce(Return(10)); int sum = obj.Add(1, 2); EXPECT_EQ(sum, 10); -
.With(matcher):设定该函数的参数应该满足matcher所指定的条件//定义 Calculator 对象的 Add 函数必须传入两个参数,且第二个参数为大于等于 0 的数 EXPECT_CALL(obj, Add(2, Ge(0))); int sum = obj.Add(2, -1); -
.After(mock)/.After(delay):设定该函数调用的时机,要求mock应该已经被满足或delay时间后调用//定义当 Calculator 对象的 Add 函数被调用时应该在 Multiply 函数后面调用 EXPECT_CALL(obj, Multiply(_, _)).WillOnce(Return(6)); EXPECT_CALL(obj, Add(_, _)).After(obj, &Calculator::Multiply); obj.Multiply(2, 3); int sum = obj.Add(1, 2);//定义 Calculator 对象的 Add 函数应该延迟 100ms 后执行 EXPECT_CALL(obj, Add(_, _)).After(100); std::this_thread::sleep_for(std::chrono::milliseconds(100)); int sum = obj.Add(1, 2);
更多预期行为,见链接gMock for Dummies | GoogleTest。