关于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是否满足
}
相关推荐
智者知已应修善业15 分钟前
【编写函数求表达式的值】2024-4-3
c语言·c++·经验分享·笔记·算法
田里的水稻21 分钟前
C++_python_相互之间的包含调用方法
c++·chrome·python
HABuo34 分钟前
【Linux进程(四)】进程切换&环境变量深入剖析
linux·运维·服务器·c语言·c++·ubuntu·centos
工口发动机43 分钟前
ABC440DEF简要题解
c++·算法
带土11 小时前
4. C++ static关键字
开发语言·c++
橘颂TA1 小时前
【Linux】死锁四条件的底层逻辑:从锁冲突到 STL 组件的线程安全实践(Ⅵ)
linux·运维·服务器·c++·死锁
C++ 老炮儿的技术栈1 小时前
什么是通信规约
开发语言·数据结构·c++·windows·算法·安全·链表
云泽8082 小时前
C++ STL 容器深度解析:deque 结构剖析与 priority_queue 的核心原理及模拟实现
开发语言·c++
码农小韩2 小时前
基于Linux的C++学习——动态数组容器vector
linux·c语言·开发语言·数据结构·c++·单片机·学习
hui函数2 小时前
如何解决 pip install 编译报错 g++: command not found(缺少 C++ 编译器)问题
开发语言·c++·pip