Search code examples
c++unit-testingboostturtle-mock

Mocking elements of an array in turtle mock


I need to mock elements of an array in turtle mock. Unfortunately, because the turtle mock macro MOCK_BASE_CLASS adds extra "gunk", e.g. mock::object, mock::detail::base<> etc, the sizes of the base and mock types are no longer identical. Therefore, and pointer indexing fails when a pointer to a base class points to an array of the mock type, as shown below.

#define BOOST_TEST_MODULE First_TestSuite
#include <boost/test/included/unit_test.hpp>
#include <turtle/mock.hpp>
#include <iostream>

struct Foo
{
    int data = 42;
};

MOCK_BASE_CLASS(mockFoo , Foo)
{
};

BOOST_AUTO_TEST_CASE( Demo )
{
    mockFoo mf[10];
    Foo* b = mf;

    std::cout << "b[1].data = " << b[1].data << std::endl;
    BOOST_CHECK(b[1].data == 42);
    //BOOST_CHECK(sizeof(mockFoo) == sizeof(Foo));  // Also fails. This is the culprit
}

Execution output

Running 1 test case...
b[1].data = 32764
Test047b.cpp(23): error: in "Demo": check b[1].data == 42 has failed

*** 1 failure is detected in the test module "First_TestSuite"

I'd appreciate suggestions on how to solve this problem. Please note that I cannot change mocking framework and the base type has to be a pointer, so that it store a pointer to an array to the base or the mock type.


Update

The following example is closer to the problem that I encountered. Using MOCK_BASE_CLASS is practically inevitable in order to mock a virtual function. I am aware that the problem is storing an array of mockFoo as Foo. I have now found a solution, which I will post subject to feedback.

struct Foo
{
    int value = 42;
    int func(){ return value;}
    virtual void unused(){}
};

MOCK_BASE_CLASS(mockFoo , Foo)
{
    MOCK_METHOD(unused , 0 , void());
};

struct Bar
{
    Bar(Foo* f) : foo(f)
    {
    }

    Foo* foo;
};

BOOST_AUTO_TEST_CASE( Demo )
{
    mockFoo mf[10];
    Bar bar(mf); // uh-oh! 

    int value = bar.foo[1].func();
    std::cout << "bar.foo[1].func() = " << value << std::endl;
    BOOST_CHECK(42 == value);
}

Result

Running 1 test case...
bar.foo[1].func() = -960497840
Test047d.cpp(35): error: in "Demo": check 42 == value has failed

*** 1 failure is detected in the test module "First_TestSuite"

Solution

  • My suspicions were confirmed: a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type (source). Accordingly I created a wrapper/adapter which I call indexer to manage the indexing of the correct type, as shown below.

    struct Foo
    {
        int value = 42;
        int func(){ return value;}
        virtual void unused(){}
    };
    
    MOCK_BASE_CLASS(mockFoo , Foo)
    {
        MOCK_METHOD(unused , 0 , void());
    };
    
    class FooIndexer
    {
    public:
        typedef Foo& (FooIndexer::*IndexerFunc)(int index);
    
        template<typename T>
        FooIndexer(T (&array)[10])
            : foo(array)
            , indexerFunc( &FooIndexer::indexer<T> )
        {
        }
    
        template<typename T>
        Foo& indexer(int index)
        {
            T* array = static_cast<T*>(foo);
            return array[index];
        }
        
        Foo& operator[](int index)
        {
            return (this->*(indexerFunc))(index);
        }
    
    private:
        Foo* foo;
        IndexerFunc indexerFunc = nullptr;
    };
    
    struct Bar
    {
        template<typename T>
        Bar(T (&f)[10]) : foo(f)
        {
        }
    
        FooIndexer foo;
    };
    
    BOOST_AUTO_TEST_CASE( Demo )
    {
        mockFoo mf[10];
        Bar bar(mf);
    
        int value = bar.foo[1].func();
        std::cout << "bar.foo[1].func() = " << value << std::endl;
        BOOST_CHECK(42 == value);
    }
    

    Execution output

    Running 1 test case...
    bar.foo[1].func() = 42
    
    *** No errors detected