关于stub和mock

第一部分:C/C++中的Mock和Stub区别

基础概念

在C/C++单元测试中:

  • Stub(打桩):提供预设的返回值

  • Mock(模拟):验证函数调用行为

实际代码示例

假设我们有一个文件操作模块:

复制代码
// file_operations.h
#ifndef FILE_OPERATIONS_H
#define FILE_OPERATIONS_H

#include <stdbool.h>

typedef struct {
    bool (*file_exists)(const char* filename);
    int (*read_file)(const char* filename, char* buffer, int size);
} FileOperations;

bool process_config_file(const char* filename, FileOperations* ops);

#endif

// file_operations.c
#include "file_operations.h"
#include <stdio.h>
#include <string.h>

bool process_config_file(const char* filename, FileOperations* ops) {
    if (!ops->file_exists(filename)) {
        return false;
    }
    
    char buffer[256];
    int bytes_read = ops->read_file(filename, buffer, sizeof(buffer));
    return bytes_read > 0;
}

Stub(打桩)示例:

复制代码
// test_stub.c
#include "file_operations.h"
#include <assert.h>

// Stub 函数 - 只提供预设返回值,不关心调用细节
static bool stub_file_exists(const char* filename) {
    return true; // 总是返回文件存在
}

static int stub_read_file(const char* filename, char* buffer, int size) {
    const char* config_content = "timeout=30\nretries=3";
    strcpy(buffer, config_content);
    return strlen(config_content); // 返回预设内容
}

void test_with_stubs() {
    FileOperations stubs = {
        .file_exists = stub_file_exists,
        .read_file = stub_read_file
    };
    
    bool result = process_config_file("config.txt", &stubs);
    assert(result == true); // 只验证最终结果
}

Mock(模拟)示例:

复制代码
// test_mock.c
#include "file_operations.h"
#include <assert.h>
#include <string.h>

// Mock 结构体 - 记录调用信息
typedef struct {
    int file_exists_call_count;
    int read_file_call_count;
    char last_filename[256];
    bool file_exists_return;
    int read_file_return;
} FileOperationsMock;

static FileOperationsMock mock_data = {0};

// Mock 函数 - 记录调用行为
static bool mock_file_exists(const char* filename) {
    mock_data.file_exists_call_count++;
    strncpy(mock_data.last_filename, filename, sizeof(mock_data.last_filename)-1);
    return mock_data.file_exists_return;
}

static int mock_read_file(const char* filename, char* buffer, int size) {
    mock_data.read_file_call_count++;
    strncpy(mock_data.last_filename, filename, sizeof(mock_data.last_filename)-1);
    
    if (mock_data.read_file_return > 0) {
        const char* content = "timeout=30";
        strncpy(buffer, content, size-1);
        return strlen(content);
    }
    return mock_data.read_file_return;
}

void test_with_mocks() {
    // 重置 Mock 数据
    memset(&mock_data, 0, sizeof(mock_data));
    mock_data.file_exists_return = true;
    mock_data.read_file_return = 10;
    
    FileOperations mocks = {
        .file_exists = mock_file_exists,
        .read_file = mock_read_file
    };
    
    bool result = process_config_file("config.txt", &mocks);
    
    // Mock 验证:检查交互行为
    assert(mock_data.file_exists_call_count == 1);
    assert(mock_data.read_file_call_count == 1);
    assert(strcmp(mock_data.last_filename, "config.txt") == 0);
    assert(result == true);
}

使用Mock框架(如Google Mock)

对于C++,使用Google Mock框架更简单:

复制代码
// mock_example.cpp
#include <gmock/gmock.h>
#include <gtest/gtest.h>

class FileSystemInterface {
public:
    virtual ~FileSystemInterface() = default;
    virtual bool fileExists(const std::string& filename) = 0;
    virtual int readFile(const std::string& filename, char* buffer, int size) = 0;
};

class MockFileSystem : public FileSystemInterface {
public:
    MOCK_METHOD(bool, fileExists, (const std::string& filename), (override));
    MOCK_METHOD(int, readFile, (const std::string& filename, char* buffer, int size), (override));
};

TEST(FileProcessingTest, ProcessConfigFile) {
    MockFileSystem mock_fs;
    
    // Stubbing: 预设返回值
    EXPECT_CALL(mock_fs, fileExists("config.txt"))
        .WillOnce(testing::Return(true));
    
    EXPECT_CALL(mock_fs, readFile("config.txt", testing::_, testing::_))
        .WillOnce(testing::Return(10));
    
    // Mock验证: 自动验证调用次数和参数
    // Google Mock会验证上面的EXPECT_CALL是否满足
}
相关推荐
从零开始的代码生活_3 小时前
C++ 内存管理:从内存分区到 new/delete 底层原理
开发语言·c++·后端
aaPIXa6224 小时前
C++ 用 13 条规则让模型写出安全现代 C++
java·c++·安全
枕星而眠5 小时前
【数据结构】红黑树入门指南
运维·数据结构·c++·后端
2601_949817725 小时前
C++指针与引用深度精讲:底层原理、差异对比与高阶实战陷阱
java·jvm·c++
烟锁池塘柳06 小时前
【C/C++】解决C++控制台输出中文乱码问题
c语言·开发语言·c++
zh路西法7 小时前
【10天速通Navigation2】(九):LQR最优控制器的原理推导与Nav2插件实现
c++·ros2·最优控制·lqr·navigation2
粘稠的浆糊7 小时前
[AtCoder - abc465_d ]X to Y题解
数据结构·c++·算法
誰能久伴不乏7 小时前
C++11 随机数生成——告别 rand()
开发语言·c++
KouFiee7 小时前
同名隐藏基础
开发语言·c++
从零开始的代码生活_8 小时前
C++ string 详解:常用接口、字符串算法与深拷贝实现
开发语言·c++·后端·学习·算法