C++ – How to Pass a Mock as std::unique_ptr to Class Under Test

cc++11polymorphismsmart-pointerunit testing

I am writing some test units using googletest and googlemock and I am stuck in a problem related to C++11 smart pointers and polymorphism.

Suppose you have those classes:

class A {
public:
    virtual void doThings() {...};
};

class B {
private:
    B(std::unique_ptr<A> a): a_(std::move(a)) {}
    std::unique_ptr<A> a_;
};

I want to test class B and so I create a mock for A and pass it in constructor:

class MockA: public A {
public:
    virtual ~MockA() {}
    MOCK_METHOD0(doThings, void());
};

TEST(...) {
    auto ma = std::make_unique<MockA>();
    B b(std::move(ma));

    // Set expectation on mock

    // call B method
}

The problem is this:

  • the mock in moved inside B instance and so expectation verification throws exception because ma is null.

My first attempt to solve it has been to change B as follows:

class B {
private:
    B(std::unique_ptr<A>& a): a_(a) {}
    std::unique_ptr<A>& a_;
};

Now B handles unique pointer references and does not use std::move. The test also changed (no more use of std::move):

TEST(...) {
    auto ma = std::make_unique<MockA>();
    B b(ma);

    // Set expectation on mock

    // call B method
}

Now the code does not compile because (if I understand correctly the error) references are not polymorphic (the unique pointer of MockA cannot be casted to unique pointer of A).

Am I missing any basics about smart pointers? Or is this the expected behaviour with unique pointers and so I have to rethink my classes (maybe using shared pointers)?

Best Answer

The easiest way would be to keep class B exactly the same as it was before, but grab the pointer from ‘ma’ before you pass it to ‘b’ in the test case. Like so…

TEST(...){
    auto ma = std::make_unique<MockA>();
    //this will be valid even after the current unique_ptr transfers ownership
    //It will be invalidated, however, if a smart pointer deletes it
    auto ( or MockA*) rawPtr = ma.get();
    B b(ma);

    //ma now points to NULL, but rawptr is still valid
    //Run analysis on mock via rawPtr
}

Changing the signature of B in such a way would not be wise. Remember, if you want a class to have exclusive ownership of a pointer, it should have its own unique_ptr for that pointer. If you change B’s unique_ptr to a reference, then B no longer technically owns the pointer even though you likely wrote the class in such a way that it acts like it owns the pointer. This could lead to some really odd behavior/crashes.

If you expect both B and A to have ownership of the pointer, meaning that the pointer NEEDS to be valid while B and another class/function are alive/running, then you should use a shared_ptr. If the pointer only needs to be valid while ONLY B is alive, then you should use a unique_ptr and make it a member of B.

Without seeing the rest of your code, I can’t say with total certainty, but I’m pretty sure you want to keep on using a unique_ptr as a member of B in this case.

EDIT

As Caleth brought out, the entire purpose of B having a unique_ptr is that it has ownership of the pointer. So if B decides it's time to delete the ptr, that memory is now invalid and any attempt to access it via the rawPtr will lead to very bad things (Undefined behaviour, crashes, etc)

Only do this if you know how B handles the unique_ptr. Only access rawPtr up until B deletes the ptr or allows its unique_ptr to go out of scope.