Including dependency with unique_ptr for mockery

I have a Foo class that uses the Bar class. Bar is used only in Foo, and Foo controls Bar, so I use unique_ptr (not a link, because I do not 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, it works great. However, I have a problem when I want unit test 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 failed because the dummy object was passed to fo Foo and the wait setting with this layout failed.

Possible DI options:

  • by shared_ptr: too much in this case (the Bar object is not shared between Foo by anything else)
  • by reference to IBar: is not an option (Bar is not stored outside Foo, so the created Bar object will be destroyed, leaving Foo with a dangling link)
  • by unique_ptr: not checked by the presented method
  • passing by value: impossible (copying will happen - the same problem as with unique_ptr).

The only solution I got is to keep the original pointer to BarMock before Foo becomes the sole 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; }; 

Is there a cleaner solution? Should I use static dependency injection (templates)?

+8
source share
2 answers

In the end, I used 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 great with gtest/gmock .

0
source

Not what I would recommend in a production environment, but the shared_ptr anti-aliasing constructor is a dirty and working solution for your business.
Minimal working example (which does not use gtest, sorry, I'm from a mobile application and cannot check 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 think your test will 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 an alias constructor do?

 template< class Y > shared_ptr( const shared_ptr<Y>& r, element_type *ptr ); 

Smoothing constructor: creates a shared_ptr that shares owner information with r but contains an unrelated and unmanaged ptr . Even if this shared_ptr is the last one out of scope, it will call a destructor for the object originally controlled by r . However, calling get() on this will always return a copy of ptr . The user must ensure that this ptr remains valid as long as this shared_ptr exists, for example, in typical use cases, when ptr is a member of an object controlled by r , or an alias (e.g. downcast) r.get()

+3
source

Source: https://habr.com/ru/post/1012178/


All Articles