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

CRTP静态多态:WebSocket服务器派生类测试能否用Mock替换基类?

解决CRTP静态多态下的测试循环引用问题

这个循环引用问题确实是CRTP在单元测试场景下的典型痛点——既要保留生产环境中基类与业务类的静态绑定,又要在测试时替换基类为Mock,直接改模板参数很容易陷入无限递归的陷阱。下面是一种既能保持CRTP性能优势,又能完美支持单元测试的解决方案:

核心思路:拆分业务逻辑与CRTP继承关系

我们将原有的业务类拆分为通用业务逻辑模板和CRTP基类模板,让业务逻辑通过模板参数注入基类;生产环境中通过闭合CRTP循环实现原有的功能,测试环境则直接注入Mock基类即可。

步骤1:定义通用业务逻辑模板

把原来WebsocketSession中的业务逻辑抽成一个模板类,它继承自传入的基类模板参数,这样可以灵活替换基类:

template<typename Base>
class WebsocketBusiness : public Base {
public:
    // 业务方法:调用基类的收发逻辑
    void someDerivedMethod() {
        // 直接调用基类方法,CRTP或Mock基类都能兼容
        this->otherBaseMethod();
    }

    // 供基类调用的业务处理方法
    void otherDerivedMethod() {
        // 这里写你的具体业务逻辑,比如解析WebSocket消息、处理业务请求等
    }
};

步骤2:定义CRTP基类模板

保留原有的WebSocket收发逻辑,基类通过CRTP调用业务类的方法:

template<typename Derived>
class WebsocketSessionBase {
public:
    // 基类方法:调用派生类的业务处理逻辑
    void someBaseMethod() {
        static_cast<Derived*>(this)->otherDerivedMethod();
    }

    // 实际的WebSocket收发实现
    void otherBaseMethod() {
        // 这里写生产环境的WebSocket发送/接收逻辑,比如调用底层API发送消息
    }
};

步骤3:生产环境闭合CRTP循环

通过定义一个具体的类来闭合CRTP的递归依赖,这完全符合CRTP的标准用法,编译器可以正常处理这种递归继承:

// 生产环境的最终会话类:业务逻辑模板 + CRTP基类
class ProdWebsocketSession : public WebsocketBusiness<WebsocketSessionBase<ProdWebsocketSession>> {};

步骤4:测试环境注入Mock基类

测试时,我们只需要定义一个Mock基类,然后注入到业务逻辑模板中即可,完全不需要修改业务代码:

#include <gmock/gmock.h>

// Mock基类:模拟WebSocket收发逻辑
class MockWebsocketBase {
public:
    // Mock基类方法,供业务类调用
    MOCK_METHOD(void, otherBaseMethod, ());

    // 如果需要测试基类调用业务方法的逻辑,可以添加这个方法
    void someBaseMethod(WebsocketBusiness<MockWebsocketBase>& business) {
        business.otherDerivedMethod();
    }
};

// 测试用的会话类型:业务逻辑模板 + Mock基类
using TestWebsocketSession = WebsocketBusiness<MockWebsocketBase>;

// 单元测试示例
TEST(WebsocketSessionTest, CallsBaseMethodOnBusinessAction) {
    TestWebsocketSession session;
    
    // 验证业务方法调用了Mock基类的收发方法
    EXPECT_CALL(static_cast<MockWebsocketBase&>(session), otherBaseMethod()).Times(1);
    session.someDerivedMethod();
}

TEST(WebsocketSessionTest, ProcessesBusinessLogicWhenBaseCalls) {
    TestWebsocketSession session;
    
    // 模拟基类触发业务逻辑处理,这里可以验证业务逻辑的执行结果
    static_cast<MockWebsocketBase&>(session).someBaseMethod(session);
    // 比如添加断言验证业务逻辑的副作用,比如消息是否被正确解析等
}

为什么这个方案能解决循环问题?

  • 生产环境中,ProdWebsocketSession继承自WebsocketBusiness<WebsocketSessionBase<ProdWebsocketSession>>,而WebsocketSessionBase的模板参数是ProdWebsocketSession本身——这是CRTP的标准用法,编译器允许这种“不完全类型”的递归引用,因为CRTP只需要进行指针转换,不需要完整的类型定义。
  • 测试环境中,TestWebsocketSession直接继承MockWebsocketBase,不存在任何递归依赖,完全独立于生产环境的CRTP基类,实现了业务逻辑与底层收发逻辑的彻底解耦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:49:51