gmock 和 gtest 的完整示例

假设我们要测试一个 UserService类,它提供了一个 RegisterUser方法。该方法的核心逻辑是验证输入参数,然后通过一个 IDatabase接口将用户信息保存到数据库。为了在不连接真实数据库的情况下测试 UserService的逻辑,我们将使用 GMock 创建一个 MockDatabase

gtest_gmock_demo/
├── CMakeLists.txt
├── include/
│ ├── database.h
│ └── user_service.h
├── src/
│ └── user_service.cpp
└── tests/
└── user_service_test.cpp

1. 定义依赖接口

(include/database.h)

首先,定义一个抽象的数据库接口。这是依赖注入的关键,也是创建Mock类的基础

复制代码
// include/database.h
#pragma once
#include <string>

class IDatabase {
public:
    virtual ~IDatabase() = default;
    virtual bool Connect(const std::string& db_uri) = 0;
    virtual bool SaveUser(const std::string& username, int age) = 0;
};
2. 实现业务逻辑

(include/user_service.h, src/user_service.cpp)

接下来,实现被测试的业务类 UserService。它在构造函数中接收一个 IDatabase指针,这使我们能够在测试时轻松地注入Mock对象

复制代码
// include/user_service.h
#pragma once
#include "database.h"
#include <string>

class UserService {
public:
    // 通过构造函数注入数据库依赖
    UserService(IDatabase* database);
    bool RegisterUser(const std::string& username, int age);

private:
    IDatabase* database_;
};

// src/user_service.cpp
#include "user_service.h"

UserService::UserService(IDatabase* database) : database_(database) {}

bool UserService::RegisterUser(const std::string& username, int age) {
    // 1. 参数校验逻辑(这是我们测试的重点之一)
    if (database_ == nullptr || username.empty() || age <= 0) {
        return false;
    }
    // 2. 通过依赖接口保存数据
    return database_->SaveUser(username, age);
}
3. 创建Mock类

(tests/user_service_test.cpp)

使用 GMock 的 MOCK_METHOD宏为 IDatabase接口创建Mock类

复制代码
// tests/user_service_test.cpp
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "../include/user_service.h"
#include "../include/database.h"

// 创建 Mock 类
class MockDatabase : public IDatabase {
public:
    MOCK_METHOD(bool, Connect, (const std::string& db_uri), (override));
    MOCK_METHOD(bool, SaveUser, (const std::string& username, int age), (override));
};
4. 编写测试用例

(tests/user_service_test.cpp)

针对不同的业务场景编写测试用例,使用 EXPECT_CALL来设定Mock对象的预期行为

复制代码
// 继续在 tests/user_service_test.cpp 中编写

// 测试用例1:用户注册成功
TEST(UserServiceTest, RegisterUser_Success) {
    // 1. 创建Mock对象
    MockDatabase mock_db;
    UserService user_service(&mock_db);

    // 2. 设定期望:SaveUser方法应被调用一次,参数为"Alice"和30,并返回true
    EXPECT_CALL(mock_db, SaveUser("Alice", 30))
        .Times(1)
        .WillOnce(testing::Return(true));

    // 3. 执行被测试方法,并断言结果
    EXPECT_TRUE(user_service.RegisterUser("Alice", 30));
}

// 测试用例2:用户名为空,注册失败,且不应调用数据库保存
TEST(UserServiceTest, RegisterUser_Fail_EmptyUsername) {
    MockDatabase mock_db;
    UserService user_service(&mock_db);

    // 设定期望:SaveUser方法不应被调用任何次数
    EXPECT_CALL(mock_db, SaveUser(testing::_, testing::_)).Times(0);

    EXPECT_FALSE(user_service.RegisterUser("", 25)); // 用户名为空
}

// 测试用例3:年龄非法,注册失败
TEST(UserServiceTest, RegisterUser_Fail_InvalidAge) {
    MockDatabase mock_db;
    UserService user_service(&mock_db);

    EXPECT_CALL(mock_db, SaveUser(testing::_, testing::_)).Times(0);

    EXPECT_FALSE(user_service.RegisterUser("Bob", -5)); // 年龄为负数
}
5. 配置CMake项目

使用 CMake 来管理项目构建和测试依赖是最佳实践

复制代码
cmake_minimum_required(VERSION 3.14)
project(GTestGMockDemo)

set(CMAKE_CXX_STANDARD 14)

# 启用测试
enable_testing()

# 包含头文件目录
include_directories(include)

# 使用 FetchContent 自动获取 Googletest 和 Googlemock
include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
FetchContent_MakeAvailable(googletest)

# 创建可执行文件
add_executable(unit_tests
    src/user_service.cpp
    tests/user_service_test.cpp
)

# 链接库
target_link_libraries(unit_tests
    gtest
    gmock
    gtest_main
)

# 将可执行文件注册为测试
gtest_discover_tests(unit_tests)
  • 测试策略 :这个示例展示了如何通过Mock隔离依赖,从而可以集中测试 UserService::RegisterUser自身的逻辑(参数校验和流程控制)。

  • ON_CALLEXPECT_CALLEXPECT_CALL用于设置期望 ,要求调用必须发生。而 ON_CALL用于设置默认行为,不强制要求调用发生,适合为那些非测试重点的依赖方法提供默认返回值。

  • 参数匹配器 :示例中使用了 testing::_,这是一个通配符,可以匹配任何参数。GMock 还提供了丰富的匹配器,如 testing::Gt(大于)、testing::StartsWith(字符串起始匹配)等,可以让你更精确地控制预期

相关推荐
以己之21 小时前
初识测试(详细篇)
单元测试·压力测试·测试
你有麻烦我有钱赚1 天前
[Tessy]函数内子函数被调用了数次,需要返回不同值
单元测试·tessy
卓码软件测评2 天前
第三方软件测试机构【Gatling源码的本地编译构建方法】
测试工具·性能优化·单元测试·测试用例
哈哈~haha2 天前
UI5_Walkthrough_Step 27: Unit Test with QUnit 单元测试QUnit
单元测试·qunit
汽车仪器仪表相关领域2 天前
ZDT-III 通用电机测试系统
数据库·算法·单元测试·压力测试·可用性测试
凌乱风雨12112 天前
Java单元测试、集成测试,区别
java·单元测试·集成测试
0和1的舞者3 天前
《软件测试分类指南:8 大维度 + 核心要点梳理》
java·软件测试·单元测试·测试·黑盒测试·白盒测试·测试分类
卓码软件测评3 天前
CMA/CNAS软件测评机构:【Gatling XPath检查:XPath语法在XML响应中的应用】
测试工具·单元测试·测试用例
汽车仪器仪表相关领域3 天前
ZRT-I 精密减速器测试系统
大数据·运维·功能测试·安全·单元测试·负载均衡·压力测试