4

I've got a class and member:

class A { B obj; public: int f(int i){return obj.g(i);} } 

Here B obj is a dependency that requires run-time creation from a file. In my unit test for class A, I wish to mock B obj, with a function g typed int(int).

How can I write my test code, to mock B obj, and then test A::f.

Thanks a lot.

2
  • You cannot mock concrete objects, class A should hold an interface implemented by B instead of a concrete instance. Commented May 10, 2016 at 6:08
  • Both other answers are incomplete. Please see my answer for a detailed explanation on how to mock class B. Also, if you find the answer helpful, consider up-voting and/or marking the answer as accepted. Commented May 10, 2016 at 14:39

4 Answers 4

6

You need to use dependency injection to achieve this. To this end, have class B inherit from an interface, and have class A hold a pointer to that interface:

class IB { public: virtual void g(int i) = 0; }; class B : public IB { public: void g(int i) { // this is your real implementation } }; 

Also, to enable dependency injection in class A, add appropriate constructor or setter method:

class A { private: IB *obj; public: A() : obj(nullptr) {} // You don't need both the constructor and setter, one is enough A(IB *pB) : obj(pB) {} // void setB(IB *pB) { obj = pB; } int f(int i) { return obj->g(i); } }; 

Now, in your production code you create an object of class B and pass it to class A object (assuming that we are using the constructor for injection):

B b; A a(&b); 

During testing phase, you create a mock class BMock and pass an object of that class to class A object:

class BMock : public IB { public: MOCK_METHOD1(g, int(int)); }; TEST(ATests, TestCase1) { BMock bmock; A a(&bmock); // Now you can set expectations on mock object, for example that g will // return 100 when it receives 50 as argument EXPECT_CALL(bmock, g(50)).WillOnce(Return(100)); // Now act by calling A::f, which will in turn call g from mock object, // and assert on function return value ASSERT_EQ(a.f(50), 100); } 
Sign up to request clarification or add additional context in comments.

Comments

2

You can't do it with the code you have. You have hardcoded your dependency inside the A class. To make mocking possible you have to use some of the dependency injection patterns. One of the possible ways is to have a pointer(better smart) to your B class and in the A constructor you will get a pointer to B with which you will initialize your inner B*. That way you will be able to put a mocked object in your tests.

Comments

2

To do this, take a pointer of B instead of object in class A and make your unit Test class (FixtureA ) as friend class in A.

class A { B *obj; public: int f(int i){return obj.g(i);} friend class FixtureA; }; 

In FixtureA.h you can have following code

class FixtureA { public: void Testf(); } 

In FixtureA.cpp

TEST_F(FixtureA , Testf) { Testf(); } void FixtureA::Testf() { A objA; objA.obj = new BMock(); objA.f(2); } 

In BMock class , you can mock the g() function.

Comments

0

I know that this question is already a few years old but I stumbled upon this in search of a better solution for exactly the same problem. Indeed, I do have two possible solutions - none of them is elegant or simple but they do work so I want to share them here in case someone else still has the same problem.

I'll assume you're using gcc and ld here but of course those methods should work with any other compiler, too (at least the second one).

Secondly, I'll assume, you have 4 files by now, A.hpp, A.cpp, B.cpp and B.hpp with the following content (of course, if you combine A.hpp and A.cpp, it will work as well...):

A.hpp:

#include "B.hpp" class A { B obj; public: int f(int i); }; 

A.cpp:

#include "A.hpp" int A::f(int i){ return obj.g(i); } 

B.hpp:

class B { public: int g(int i); }; 

B.cpp includes the implementation of B::g().

Method 1 is possible, if you have a folder structure where B.hpp is not in the same folder as A.cpp or if you replace line 1 in A.cpp by #include <B.hpp> (I think this is compiler/preprocessor dependant but I'm not sure on that one. Afaik, gcc always searches in . first, if you include with ")

In this case, you can create another B.hpp:

#include <gmock/gmock.h> class B { public: MOCK_METHOD(int, g, (int), ()); }; 

Now, you must change your commandline to something like

g++ A.cpp -I./Testing/B -o A.o

where the new B.hpp lies in ./Testing/B. Furthermore, you must remove B.cpp from your pipeline when building the tests.

Be aware that this method changes what's included in your original cpp file and therefore changes its object. If you don't be careful enough there is a certain chance that your test behaves different to your real system!

Method 2: You can use that one if B.hpp is in the same folder as A.cpp and you have a code using #include "B.hpp" . In this case, the compiler will most probably use the B.hpp inside the source folder instead of the one given with the -I flag. I consider this method superior as it doesn't change the compile unit A.o but on the other hand its much more work to do.

Create two files: Bmock.hpp:

#include <gmock/gmock.h> class Bmock{ public: MOCK_METHOD(int,g,(int),()); }; 

B.cpp

#include <B.hpp> #include <Bmock.hpp> extern Bmock* bmock; int B::g(int i){ return bmock->g(i); } 

Now, during your linking step, have to replace your original B.o file with the new one.

Inside your Test Suite:

#include <Bmock.hpp> #include <A.hpp> #include <gtest/gtest.h> Bmock* bmock; class UT_A:public ::testing::Test{ public: void SetUp() override{ bmock=new bmock(); } void TearDown() override{ delete bmock; } A uut; }; TEST_F(UT_A,test_g){ EXPECT_CALL(*bmock,g(5)).WillOnce(Return(3)); ASSERT_EQ(uut.f(5),3); } 

Using this method, your compile unit A.o can be left untouched and should therefore behave as in the real system.

I hope this is of use for someone!

Greetings

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.