关于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是否满足
}
相关推荐
肆忆_16 小时前
# 用 5 个问题学懂 C++ 虚函数(入门级)
c++
不想写代码的星星20 小时前
虚函数表:C++ 多态背后的那个男人
c++
端平入洛3 天前
delete又未完全delete
c++
端平入洛4 天前
auto有时不auto
c++
哇哈哈20215 天前
信号量和信号
linux·c++
多恩Stone5 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
蜡笔小马5 天前
21.Boost.Geometry disjoint、distance、envelope、equals、expand和for_each算法接口详解
c++·算法·boost
超级大福宝5 天前
N皇后问题:经典回溯算法的一些分析
数据结构·c++·算法·leetcode
weiabc5 天前
printf(“%lf“, ys) 和 cout << ys 输出的浮点数格式存在细微差异
数据结构·c++·算法
问好眼5 天前
《算法竞赛进阶指南》0x01 位运算-3.64位整数乘法
c++·算法·位运算·信息学奥赛