Search code examples
c++unit-testingdependency-injectiongooglemock

Dependency injection with unique_ptr to mock


I have a class Foo that uses class Bar. Bar is used only in Foo and Foo is managing Bar, therefore I use unique_ptr (not a reference, because I don't need Bar outside of Foo):

using namespace std;
struct IBar {
    virtual ~IBar() = default;  
    virtual void DoSth() = 0;
};

struct Bar : public IBar {
    void DoSth() override { cout <<"Bar is doing sth" << endl;};    
};

struct Foo {
  Foo(unique_ptr<IBar> bar) : bar_(std::move(bar)) {}

  void DoIt() {
    bar_->DoSth();
  }
private:
  unique_ptr<IBar> bar_;
};

So far so good, this works fine. However, I have a problem when I want to unit test the code:

namespace {
struct BarMock : public IBar {
  MOCK_METHOD0(DoSth, void());
};
}

struct FooTest : public Test {
  FooTest() : barMock{ make_unique<BarMock>() }, out(std::move(barMock)) {}

  unique_ptr<BarMock> barMock;
  Foo out;
};

TEST_F(FooTest, shouldDoItWhenDoSth) {
  EXPECT_CALL(*barMock, DoSth());

  out.DoIt();
}

The test fails because the mock object was transfered fo Foo, and setting an expectation on such mock fails.

Possible options of DI:

  • by shared_ptr: is too much in this case (Bar object is not shared between Foo any anything else)
  • by reference to IBar: isn't an option (Bar is not stored outside Foo, so the Bar object created would be destructed leaving Foo with dangling reference)
  • by unique_ptr: isn't testable in the presented way
  • by passing by value: isn't possible (copying will occure - same issue as with unique_ptr).

The only solution I got is to store raw pointer to BarMock before Foo become solely owner of BarMock, i.e.:

struct FooTest : public Test {
  FooTest() : barMock{new BarMock} {
    auto ptr = unique_ptr<BarMock>(barMock);
    out.reset(new Foo(std::move(ptr)));
  }

  BarMock* barMock;
  unique_ptr<Foo> out;
};

Isn't there a cleaner solution? Do I have to use static dependency injection (templates)?


Solution

  • After all, I ended up using this approach everywhere:

    struct FooTest : public Test {
      FooTest() : barMock{new BarMock} {
        auto ptr = unique_ptr<BarMock>(barMock);
        out.reset(new Foo(std::move(ptr)));
      }
    
      BarMock* barMock;
      unique_ptr<Foo> out;
    };
    

    and it works fine with gtest/gmock.