Search code examples
c++boostembeddedstate-machinegooglemock

GMock / Virtual Functions / Boost MSM how to correctly combine?


I need your help. the following:

A class X defines methods that compose commands. These commands are sent to a UART device. Class X must be static. The methods of class X must be called in a state machine (Boost MSM) according to the state.

Now I have to write a test that checks whether the methods are called correctly. This way I can also check whether all transition tables are correct.

I use GMock for testing. However, the class from which the mock class inherits must be virtual (according to GMock). This is not possible in the productive code (embedded device, little memory, etc.).

That's why I decided to write a dummy of class X. This has the same methods as original Class X, but in this case they can be virtual because the test is executed on a computer.

Now the test has to create an instance of the state machine, which is not a problem.

The real problem is that I have to inherit from the dummy classX in the state machine to call the functions so that the mock can check whether the methods can be called correctly.

Unfortunately, this isn't possible because VTables are created in production code as soon as I inherit from the DummyClassX.

Unfortunately, I can't think of a solution as to how I should manage to test the state machine and the function calls. Is there a trick to prevent VTables from being created?

Here I have created another sketch to make it easier to understand.

enter image description here


Solution

  • However, the class from which the mock class inherits must be virtual (according to GMock).

    Says who? ;-) You seem to have skipped Mocking Non-virtual Methods in gtest's docs.

    That will require some of the existing code to be reworked into templates, but for sure it can be done.

    Imagine the following class

    struct Dep
    {
        void doStuff() const{};
    };
    

    and a one dependent on it:

    struct S
    {
        S(const D& d_) : d(d_) {}
        void doStuff() const { d.doStuff(); }
        Dep d;
    };
    
    
    

    The latter can be reworked to the following form

    template<typename D>
    struct S
    {
        S(const D& d_) : d(d_) {}
        void doStuff() const { d.doStuff(); }
        D d;
    };
    

    so that the dependency can be injected.

    So, let's create a mock:

    struct DMock
    {
        MOCK_METHOD(void, doStuff,(), (const));
    };
    

    The problem is though, that mocks are not copyable, and trick with std::ref will not work here unless passing a function object.

    Therefore, an extra level of indirection has to be provided. If your code accepts dependencies via pointers or references, you might want to skip this step.

    struct DMockWrapper
    {
        DMock* impl;
    
        void doStuff() const { impl->doStuff(); }
    };
    

    Therefore, the whole test will look like that:

    TEST(X, Y)
    {
        DMock dm;
        S<DMockWrapper> s{DMockWrapper{&dm}};
        EXPECT_CALL(dm, doStuff);
        s.doStuff();
    }
    

    https://godbolt.org/z/nefhjzbKd