Dependency injection with unique_ptr to mock
Solution 1:
Not something I would recommend in production environment actually, but aliasing constructor of shared_ptr
represents maybe a dirty and working solution for your case.
A minimal, working example (that doesn't use gtest, sorry, I'm from mobile app and can't test it directly):
#include<memory>
#include<iostream>
#include<utility>
struct IBar {
virtual ~IBar() = default;
virtual void DoSth() = 0;
};
struct Bar : public IBar {
void DoSth() override { std::cout <<"Bar is doing sth" << std::endl;};
};
struct Foo {
Foo(std::unique_ptr<IBar> bar) : bar(std::move(bar)) {}
void DoIt() {
bar->DoSth();
}
private:
std::unique_ptr<IBar> bar;
};
int main() {
std::unique_ptr<Bar> bar = std::make_unique<Bar>();
std::shared_ptr<Bar> shared{std::shared_ptr<Bar>{}, bar.get()};
Foo foo{std::move(bar)};
shared->DoSth();
foo.DoIt();
}
I guess your test would become something like this:
struct BarMock: public IBar {
MOCK_METHOD0(DoSth, void());
};
struct FooTest : public testing::Test {
FooTest() {
std::unique_ptr<BarMock> bar = std::make_unique<BarMock>();
barMock = std::shared_ptr<BarMock>{std::shared_ptr<BarMock>{}, bar.get()};
out = std::make_unique<Foo>{std::move(bar)};
}
std::shared_ptr<BarMock> barMock;
std::unique_ptr<Foo> out;
};
TEST_F(FooTest, shouldDoItWhenDoSth) {
EXPECT_CALL(*barMock, DoSth());
out->DoIt();
}
What does the aliasing constructor do?
template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type *ptr );
The aliasing constructor: constructs a
shared_ptr
which shares ownership information withr
, but holds an unrelated and unmanaged pointerptr
. Even if thisshared_ptr
is the last of the group to go out of scope, it will call the destructor for the object originally managed byr
. However, callingget()
on this will always return a copy ofptr
. It is the responsibility of the programmer to make sure that thisptr
remains valid as long as thisshared_ptr
exists, such as in the typical use cases whereptr
is a member of the object managed byr
or is an alias (e.g., downcast) ofr.get()
Solution 2:
You can keep a reference to the mocked object before passing it to the constructor. I think it makes the code a tad bit brittle, due to member initialization ordering, but it is clearer semantically what it means. Ownership of BarMock
still belongs solely to Foo
, with a reference handle kept by FooTest
(similar to this answer).
Basically the same as your answer, but using reference instead of a raw pointer
class FooTest : public ::testing::Test
{
protected:
FooTest() :
bar_mock_ptr(std::make_unique<BarMock>()),
bar_mock(*bar_mock_ptr),
foo(std::move(bar_mock_ptr))
{}
private:
// This must be declared before bar_mock due to how member initialization is ordered
std::unique_ptr<BarMock> bar_mock_ptr; // moved and should not be used anymore
protected:
BarMock& bar_mock;
Foo foo; //ensure foo has the same lifetime as bar_mock
}