c++unit-testingcrtpstatic-polymorphism

CRTP static polymorphism: is it possible to replace the base class with a mock?


I use CRTP static polymorphism in a websocket server to decouple networking code from business logic. The base class calls methods on the derived to process messages, the derived in turn calls the base to send/receive. It works like charm, looks something like this:

template<typename Derived>
class WebsocketSessionBase {
    // basic websocket send/receive stuff

    void someBaseMethod() {
        static_cast<Derived*>(this)->otherDerivedMethod();
    }
};

class WebsocketSession : public WebsocketSessionBase<WebsocketSession> {
    // specific business logic

    void someDerivedMethod() {
        otherBaseMethod();
    }
};

Now comes unit testing. Since the code is decoupled I would like the test functionality in the classes separately.

Testing the base class is simple:

class TestSession : public WebsocketSessionBase<TestSession> {
    // same interface as WebsocketSession, but test code, cool!
};

But how do I test derived class? Adding a Base template parameter came to my mind, which makes test code ok (Base is a mock class). But I end up having 2 template classes referring to each other in the production version... :(

template<typename Base>
class TestableWebsocketSession : public Base {
};

using TestedWebsocketSession = TestableWebsocketSession<MockBase>;
using ProdWebSocketSession = TestableWebsocketSession<WebsocketSessionBase<... // infinite loop - now what!?

Is it possible to come over this?


Solution

  • I don't know if it's worth it, but you could make WebsocketSession a class template taking a template template parameter :

    template<class T>
    struct WebsocketSessionBase { /*...*/ };
    
    template<template<class> class B>
    struct WebsocketSessionDerived: B<WebsocketSessionDerived<B>>{ /*...*/ };
    
    using WebsocketSession = WebsocketSessionDerived<WebsocketSessionBase>;
    
    using DerivedTestSession = WebsocketSessionDerived<WebsocketSessionMockBase>;
    struct BaseTestSession : WebsocketSessionBase<BaseTestSession>{ /*...*/ };