Search code examples
c++unit-testinggoogletestgooglemock

Using googlemock with fake impls of non-virtual functions


I've got legacy code with some unit tests based on googlemock framework. While I was trying to extend the unit tests with some new scenarios I met the following problem:

class D
{
public:
  void pubMethod1();
  int pubMethod2();
  // There are pretty much non-virtual methods, both public and private
  ...
protected:
  uint method3();
  void method4();
  ...
  // Some class members are here
};

class SUT
{
public:
  ...
protected:
   D _dep;
};

The SUT class (software under test) should be tested, its implementation is defined in file sut.cpp. The SUT depends on D class, whose implementation is in file d.cpp. To decrease linker dependencies I would not like to add d.cpp to the test, so there are many 'undefined symbol' errors against the D's members when I link the test. To eleminate the errors and provide predictable behaviour I'm going to create fake implementations for the D's methods in my test. However I'm still not able to use it with all the googlemock's power until the D's methods are virtual.

I like an idea to use WillOnce, AtLeast, WillRepeatedly, Invoke, etc. functions from the googlemock framework, because it makes unit test creation easier. The problem is that I don't like an idea to change the D's interface, turning its methods into virtual ones. Is it possible to use the googlemock functions with the fake implementations I'm going to create for the D's methods?

NOTE: I already considered the solution with templated SUT class, however I'm wondering if other solution exists.


Solution

  • First of all - the best would be to redesign your SUT class to have D injected via some abstract interface. Because the workaround I am describing below is really tricky - so not so easy to maintain and understand in future...


    If you are going to make fake implememtation of your D class in UT target - then you can make Mock class for D: DMock. This DMock will not be related to D - not derived from it - but it needs to be paired with real/fake D objects.

    So - see the example:

    Create DMock - that imitates the D interface (note that you should mock only public functions - because your SUT uses only public ones):

    class DMock 
    {
    public:
        MOCK_METHOD0(pubMethod1, void ());
        MOCK_METHOD0(pubMethod2, int ());
    };
    

    Pair your real (but fake) D objects with DMock objects - something like this:

    class DMockRepo
    {
    public:
        // for UT
        void addMockToUse(DMock* dMock) { freeMock.push_back(dMock); }
    
        // for implementing D fake methods
        DMock& getMock(D* original)
        {
            // TODO: use more sophisticated way to add mock to map...
            if (not usedMock[original])
            {
               usedMock[original] = freeMock.front();
               freeMock.pop_front();
            }
            return *useddMock[original];
        }
        static DMockRepo& getInstance() { return instance; } //singleton
    private:
        DMockRepo() {} // private
        static DMockRepo instance;
        std::map<D*,DMock*> usedMock;
        std::deque<DMock*> freeMock; 
    };
    

    Use mock to create fake implementation of public methods of D class:

    void D::pubMethod1()
    {
        DMockRepo::getInstance().getMock(this).pubMethod1();
    } 
    // 
    

    Non-public methods are irrelevant - so do whatever you like...

    And use DMockRepo to set expectation on your D objects:

    TEST(Usage,Example)
    {
       DMock dMock;
       DMockRepo::getInstance().addMockToUse(&dMock);
       SUT sut; // you should know how many D objects SUT needs - I assume just one
    
       EXPECT_CALL(dMock, pubMethod1());
       sut.doSomethingThatCallsDpubMethod1();
    }