关于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是否满足
}
相关推荐
Yupureki7 小时前
从零开始的C++学习生活 18:C语言复习课(期末速通)
c语言·数据结构·c++·学习·visual studio
永远有缘7 小时前
四种编程语言常用函数对比表
java·开发语言·c++·python
C++_girl7 小时前
c++、java/python语言有什么区别?为什么c++更快?
java·开发语言·c++
艾莉丝努力练剑7 小时前
【Linux权限 (二)】Linux权限机制深度解析:umask如何决定默认权限与粘滞位的妙用
大数据·linux·服务器·c++·ubuntu·centos·1024程序员节
彩妙不是菜喵7 小时前
基于C语言上,面向对象语言:C++基础(学完C语言后再看)
c语言·开发语言·c++
夜晚中的人海8 小时前
【C++】位运算算法习题
开发语言·c++·算法
枫叶丹49 小时前
【Qt开发】布局管理器(一)-> QVBoxLayout垂直布局
开发语言·c++·qt
superior tigre9 小时前
(huawei)5.最长回文子串
c++·算法
Qt程序员9 小时前
C++ 虚函数的使用开销以及替代方案
c++·c++设计模式·c/c++·c++虚函数