google test C++ 单元测试 demo

demo项目

https://github.com/shensunbo/google_test_insight

gtest 参考文档

  1. 官方手册
  2. 参考项目

简介

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));  // 设置虚拟函数调用的期望返回值
    

注意事项

  1. 测试用例的名字中不能使用下划线
  2. mock 的对象不允许拷贝,只能通过指针或引用传递
  3. 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);
}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值