demo项目
https://github.com/shensunbo/google_test_insight
gtest 参考文档
- 官方手册
- 参考项目
简介
Google Test (gtest) 是 Google 提供的一个用于 C++ 测试的框架,提供了gtest和gmock, 一个最基本的测试用例如如下:
bool returnTrue(){
return true;
}
TEST(DemoTest, returnTrueTest){
EXPECT_TRUE(returnTrue);
EXPECT_EQ(true, returnTrue);
}
常用命令
TEST
:定义一个测试用例。TEST_F
:定义一个测试夹具类(用于共享测试用例的设置和清理代码)。EXPECT_TRUE / ASSERT_TRUE
:用于检查条件是否为真,二者的区别在于失败时的处理方式,EXPECT_TRUE
只会记录失败信息,而不会中止当前测试用例,而 ASSERT_TRUE 则会中止当前测试用例的执行。EXPECT_FALSE / ASSERT_FALSE
:用于检查条件是否为假,与上述宏的区别相似。EXPECT_EQ / ASSERT_EQ
:用于检查两个值是否相等。EXPECT_NE / ASSERT_NE
:用于检查两个值是否不相等。EXPECT_STREQ / ASSERT_STREQ
:用于检查两个 C 风格字符串是否相等。EXPECT_STRNE / ASSERT_STRNE
:用于检查两个 C 风格字符串是否不相等。EXPECT_STRCASEEQ / ASSERT_STRCASEEQ
:用于检查两个 C 风格字符串是否相等,不区分大小写。EXPECT_STRCASENE / ASSERT_STRCASENE
:用于检查两个 C 风格字符串是否不相等,不区分大小写。
编译命令
enable_testing()
# 开启测试支持add_executable(runTests test_your_code.cpp)
# 这里的 test_your_code.cpp 是包含了您的测试的文件add_test(NAME runTests COMMAND runTests)
# 添加一个测试,用于当使用 ctest 命令运行的时候可以执行当前测试
gmock
Google Mock(简称为 gmock)是Google C++ 测试框架(Google Test)的一部分,它是一个用于 C++ 的用于测试的模拟框架。它允许您使用一种简单的方式创建模拟对象,并使用这些模拟对象在测试中模拟未定义的行为。
Google Mock 可以帮助您模拟整个对象,包括内部的私有成员、类方法,还可以设置它们的行为,返回模拟的函数结果,甚至使用正则表达式指定参数的匹配条件。主要用来模拟依赖项的行为,一般要求被测试的类中依赖项的引入方式是采用依赖注入(dependency injection )
的方式,否则将很难使用gmock对依赖项的行为进行模拟,gmock的实现方式依赖于多态。
一个使用gmock的测试case如下
#include <gmock/gmock.h>
class Engine {
public:
virtual int start() = 0;
virtual int stop() = 0;
virtual ~Engine() {}
};
class MockEngine : public Engine {
public:
MOCK_METHOD(int, start, (), (override));
MOCK_METHOD(int, stop, (), (override));
};
class Car {
private:
Engine* engine;
public:
Car(Engine* _engine) : engine(_engine) {}
int start() {
return engine->start();
}
int getEngineStatus() {
return engine->stop();
}
};
TEST(CarTest, EngineStartTest) {
MockEngine mockEngine;
EXPECT_CALL(mockEngine, start()).WillOnce(testing::Return(1));
// 使用 mock 对象执行测试
Car car(&mockEngine);
car.start();
// 断言期望结果
ASSERT_EQ(1, car.getEngineStatus());
}
常用命令
MOCK_METHOD
:用于声明一个虚拟函数的模拟。第一个参数是函数的返回类型,第二个参数是函数名,第三个参数是函数的参数列表,第四个参数是修饰符(override),(const, override)
等)EXPECT_CALL
:Google Mock 框架中的一个宏,用于规范模拟对象的行为。它允许您设置对虚拟函数的调用所期望的行为,并且可以在测试期间验证这些调用是否如预期所执行。EXPECT_CALL(mockObject, virtualFunction(parameter)) .Times(1) // 设置期望的调用次数 .WillOnce(Return(5)); // 设置虚拟函数调用的期望返回值
注意事项
- 测试用例的名字中不能使用下划线
mock
的对象不允许拷贝,只能通过指针或引用传递EXPECT_CALL
中的moc
对象和实际使用的mock
对象必须指向同一个,在测试类中实例化一个mock
对象不会满足EXPECT_CALL
典型用法Demo
TEST_F (定义测试夹具(fixture))
// 定义一个需要测试的类
class MyData {
public:
int getValue() { return value; }
void setValue(int v) { value = v; }
private:
int value = 0;
};
// 定义一个测试类
class MyTestFixture : public ::testing::Test {
protected:
void SetUp() override {
// 在测试用例开始时进行一些初始化
data.setValue(42);
}
void TearDown() override {
// 在测试用例结束时进行一些清理
}
MyData data; // 声明测试用例需要使用的类成员
};
// 使用 TEST_F 定义测试用例
TEST_F(MyTestFixture, TestValue) {
// 在这里进行测试
ASSERT_EQ(data.getValue(), 42);
}
TEST_F(MyTestFixture, TestModification) {
// 另一个测试用例
data.setValue(100);
ASSERT_EQ(data.getValue(), 100);
}
MATCHER(匹配器)
gmock测试类的接口时,如果接口的输入参数是结构体或者类等复杂类型,可能需要用到匹配器,来验证预期结果和实际结果
// 假设有一个类 SomeClass
class SomeClass {
public:
virtual ~SomeClass() {}
virtual void ProcessData(const Data& data) = 0;
};
// 假设有一个结构体 Data
struct Data {
int id;
std::string name;
};
// 定义一个接受 Data 结构体对象的自定义匹配器
// expected 是期望的输入参数,参数可以有多个
// arg 是实际捕获到的参数
MATCHER_P(IsSameData, expected, "description_here") {
// 使用自定义匹配器来检查 Data 对象的 id 和 name
return arg.id == expected.id && arg.name == expected.name;
}
// 编写测试用例
TEST(SomeClassTest, ProcessDataTest) {
// 创建 SomeClass 的模拟对象
SomeClassMock mock;
// 设置预期调用
Data expected_data{1, "TestName"};
EXPECT_CALL(mock, ProcessData(IsSameData(expected_data)));
// 调用被测对象的函数
// ... 调用涉及到 mock 对象的其他逻辑
// 验证 mock 的预期调用是否发生
::testing::Mock::VerifyAndClearExpectations(&mock);
}
Note: 以上这种简单的结构体可以不使用自定义匹配器,复杂的有嵌套的结构体的类型需要使用匹配器,对于上述这种结构体类型,也可以使用* Field *
EXPECT_CALL(mock, ProcessData(Field(&Data::id, expected_data.id) && Field(&Data::name, expected_data.name)));
使用GMock模拟non-virual 方法
使用gmock模拟非虚函数,是使用模板来实现的,基于编译时绑定,在实际使用依赖项时,使用实际的模板参数,在单元测试中使用mock的模板参数
// A simple packet stream class. None of its members is virtual.
class ConcretePacketStream {
public:
void AppendPacket(std::string& new_packet){
std::cout << new_packet << std::endl;
}
};
// A mock packet stream class. It inherits from no other, but defines AppendPacket
class MockPacketStream {
public:
MOCK_METHOD(void, AppendPacket, (std::string& new_packet), ());
};
//使用依赖项的方法和类需要重构为模板,且当依赖项为类成员时,这个成员必须能通过外部进行注入,即使用指针或者引用
template <typename T>
class UsePacketStream{
public:
void AddPacket(std::string& new_packet, T& _stream){
_stream.AppendPacket(new_packet);
}
};
//实际使用依赖项的地方
void foo(){
UsePacketStream<ConcretePacketStream> useStream;
std::string new_packet = "Hello World!";
useStream.AddPacket(new_packet);
}
//单元测试
TEST(HiPerfDependencyInjectionTest, MockPacketStreamTest) {
MockPacketStream stream;
std::string mockPackage = "Hello GMock!";
EXPECT_CALL(stream, AppendPacket(Eq(mockPackage)))
.Times(1);
UsePacketStream<MockPacketStream> useStream;
useStream.AddPacket(mockPackage, stream);
}