You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试Mock socket全局函数失败:预期调用未触发

解决方案:不修改生产代码Mock全局Socket函数

针对你遇到的问题,核心是通过链接器符号覆盖或预编译宏替换,在不改动生产代码的前提下替换系统原生的socket()/send()/recv()/close()函数,结合GMock完成单元测试。以下是具体实现方案:

方法1:链接器符号覆盖(推荐,适配所有场景)

C语言全局函数默认是弱符号,测试代码中定义同名函数时,链接器会优先使用测试中的实现,自动覆盖系统库的原生函数。我们可以把这些函数转发到GMock对象上。

步骤1:定义Socket Mock类

#include <gmock/gmock.h>
#include <sys/socket.h>

class SocketMock {
public:
    MOCK_METHOD(int, socket, (int domain, int type, int protocol), ());
    MOCK_METHOD(ssize_t, send, (int sockfd, const void *buf, size_t len, int flags), ());
    MOCK_METHOD(ssize_t, recv, (int sockfd, void *buf, size_t len, int flags), ());
    MOCK_METHOD(int, close, (int fd), ());
};

// 全局指针,用于转发系统函数调用到Mock对象
extern SocketMock* g_socket_mock;

步骤2:实现全局转发函数

必须用extern "C"包裹,保证函数名遵循C语言链接约定,避免C++编译器修改函数名导致无法覆盖:

#include "socket_mock.h"

SocketMock* g_socket_mock = nullptr;

extern "C" {
int socket(int domain, int type, int protocol) {
    return g_socket_mock->socket(domain, type, protocol);
}

ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
    return g_socket_mock->send(sockfd, buf, len, flags);
}

ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
    return g_socket_mock->recv(sockfd, buf, len, flags);
}

int close(int fd) {
    return g_socket_mock->close(fd);
}
}

步骤3:编写测试用例

#include "client_connection.h"
#include "socket_mock.h"
#include <gmock/gmock.h>

TEST(ClientConnectionTest, ConstructorCreatesSocket) {
    SocketMock mock;
    g_socket_mock = &mock;

    // 设置预期:调用AF_INET域的流式socket,返回模拟的文件描述符123
    EXPECT_CALL(mock, socket(AF_INET, SOCK_STREAM, 0))
        .WillOnce(testing::Return(123));

    // 构造ClientConnection,此时会调用Mock的socket函数
    ClientConnection conn;

    // 重置全局指针,避免污染其他测试用例
    g_socket_mock = nullptr;
}

TEST(ClientConnectionTest, SendDataCallsSendFunction) {
    SocketMock mock;
    g_socket_mock = &mock;

    EXPECT_CALL(mock, socket(AF_INET, SOCK_STREAM, 0))
        .WillOnce(testing::Return(456));
    EXPECT_CALL(mock, send(456, testing::_, 5, 0))
        .WillOnce(testing::Return(5));

    ClientConnection conn;
    conn.send_data("hello", 5);

    g_socket_mock = nullptr;
}

注意事项

  • 确保测试代码的链接顺序:链接器会优先使用当前二进制中的符号,因此只要测试代码被正确链接,就能覆盖系统库的函数。
  • 若生产代码中使用了这些函数的函数指针,该方法依然有效(因为指针指向的是我们的转发函数)。

方法2:预编译宏替换(适合简单场景)

如果不需要复杂的Mock验证,仅需替换函数行为,可以用宏直接替换系统函数。但该方法无法处理函数指针场景。

在测试文件的最开头(必须在包含<sys/socket.h>或生产代码头文件之前)定义宏:

// 宏替换系统函数为Mock对象的方法
#define socket(domain, type, protocol) g_socket_mock->socket(domain, type, protocol)
#define send(sockfd, buf, len, flags) g_socket_mock->send(sockfd, buf, len, flags)
#define recv(sockfd, buf, len, flags) g_socket_mock->recv(sockfd, buf, len, flags)
#define close(fd) g_socket_mock->close(fd)

// 后续包含生产代码头文件和Mock定义
#include "client_connection.h"
#include "socket_mock.h"
#include <gmock/gmock.h>

// 测试用例编写同方法1

常见问题排查

  • Mock方法未被触发:检查是否用extern "C"包裹了转发函数,C++编译时必须保证函数名不被mangle。
  • 链接错误:确认测试代码中没有重复定义这些函数,且全局Mock指针已正确初始化。

内容的提问来源于stack exchange,提问作者headfxll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 09:37:30