Search code examples
c++unit-testingmockingshared-ptr

How to use shared_ptr to supply mock object from outside?


I want to test an app Thud, which will use a resource Foo. It will not own a concrete object but will have a pointer to a abstract resource interface (IFoo here). In production I will supply it with the actual resource implementation (FooImpl) whereas for unit test I want to send a pointer to a mock resource. How should I do this ? I've tried to write the least code just to get to point,

class IFoo
{
public:
    virtual bool Foo(bool) = 0;
};

class FooImpl : public IFoo
{
public:
    bool Foo(bool b) override
    {
        return b;
    }
};

class FooMock : public IFoo
{
public:
    MOCK_METHOD1(Foo, bool(bool));
};

class ThudTest : public ::testing::Test
{
protected:
    virtual void SetUp() {
        //foo.reset(&mock); //line#1
        foo = &mock; //line#2
    }

    FooMock mock;
    //std::shared_ptr<IFoo> foo; //line#3
    IFoo* foo; //line#4
};

class Thud
{
    //std::shared_ptr<IFoo> fooPtr; //line#5
    IFoo* fooPtr; //line#6
public:
    /*Thud(std::shared_ptr<IFoo> fooPtr_) : fooPtr{ fooPtr_ }
    {}*/ //line#7
    Thud(IFoo* fooPtr_) : fooPtr{ fooPtr_ }
    {} //line#8
    bool thud1(bool b)
    {
        return fooPtr->Foo(b);
    }
};

TEST_F(ThudTest, fooFalse)
{
    bool b = false;
    EXPECT_CALL(mock, Foo(b)).Times(1).WillOnce(Return(false));;

    Thud thud(foo);
    EXPECT_FALSE(thud.thud1(b));
}

TEST_F(ThudTest, fooTrue)
{
    bool b = true;
    EXPECT_CALL(mock, Foo(b)).Times(1).WillOnce(Return(true));;

    Thud thud(foo);
    EXPECT_TRUE(thud.thud1(b));
}

int main(int argc, char** argv) {
    // The following line must be executed to initialize Google Mock
    // (and Google Test) before running the tests.
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

So for completion I've has an overloaded constructor that takes not arguement but will do the following,

Thud():fooPtr {std::make_shared<FooImpl>()}
{}

to get the real implementation in production. But now how do I make the pointer point to the mock object. As you can see, I'm using GMock framework here. How to achieve this ?

If I comment out line #2,4,6 & 8 which uses a plain old raw pointer and uncomment and use line #1,3,5 & 7 (using the shared_ptr in question here) instead, it crashes with heap corruption after the first test case.

It works perfectly with this raw pointer.


Solution

  • You cannot do foo.reset(&mock), since then mock has two owners: its automatic storage duration, plus the shared pointer foo. Memory corruption FTW.

    You should simply allocate the FooMock dynamically and pass it in when creating the Thud instance being tested:

    class ThudTest : public ::testing::Test
    {
    protected:
        virtual void SetUp() {
            foo = std::make_shared<FooMock>();
        }
    
        std::shared_ptr<FooMock> foo;
    };
    

    You won't even need the mock member any more. Note that I've changed the type of foo to know about FooMock, so that you can access that type. If you really want foo to remain mock-unaware, do it like this:

    class ThudTest : public ::testing::Test
    {
    protected:
        virtual void SetUp() {
            mock = std::make_shared<FooMock>();
            foo = mock;
        }
    
        std::shared_ptr<FooMock> mock;
        std::shared_ptr<IFoo> foo;
    };
    

    However, this should not be necessary, since std::shared_ptr<FooMock> is implicitly convertible to std::shared_ptr<IFoo>.