简体   繁体   中英

gmock - mocking non-virtual method of the derived class

I am using VS2005 and gmock ver. 1.6 for unit testing.

I have the following code which I want to mock, but I cannot find a way to do so.

class A
{
 virtual bool foo1() = 0;
 virtual bool foo2() = 0;
};

class B : public A
{
 virutal bool foo1();
 virtual bool foo2();
 static B* getInstance(int x);
 static B* getInstance();
}

Where getInstance(int x) is just creating an instance of B and returns it. Whereas getInstance() just returns the already created instance by getInstance(int x) ;

I have the mock class,

class MockA : public A
{
 MOCK_METHOD0(foo1, bool());
 MOCK_METHOD0(foo2, bool());
}

In the sources, I am using

bool retVal = B::getInstance()->foo2()

How can I mock this behavior B::getInstance() ?

I think that you have to use linker seam to do what you want.

Let's say that we have libraryToBeMocked with the interface placed in libraryToBeMocked.hpp :

#pragma once

class Base
{
public:
    virtual bool evenCheck() = 0;
    virtual bool oddCheck() = 0;
};

class Derived : public Base
{
public:
    virtual bool evenCheck();
    virtual bool oddCheck();
    static Base* getInstance(int x);
    static Base* getInstance();

private:
    Derived(int x);

    int x;
};

You should have noticed that I have altered your design. Both getInstance method returns pointer to Base not Derived . If you want to mock the logic clearly you should mock the pure interface Derived already has some logic in it. IMHO both getInstance methods should be moved to other class anyways.

The implementation of the library is in libraryToBeMockedImpl.cpp

#include "libraryToBeMocked.hpp"

#include <memory>
#include <cstdlib>

bool Derived::evenCheck() { return x % 2 == 0; }

bool Derived::oddCheck() { return x % 2 != 0; }

namespace
{
    std::auto_ptr<Derived> current(NULL);
}

Base* Derived::getInstance(int x)
{
    current.reset(new Derived(x));

    return current.get();
}

Base* Derived::getInstance()
{
    return current.get();
}

Derived::Derived(int x) : x(x) {}

There is also the logic which you want to test. testedLibrary.hpp :

#pragma once

bool isOdd(int x);
bool isEven(int x);

implementation testedLibraryImpl.cpp :

#include "testedLibrary.hpp"

#include "libraryToBeMocked.hpp"

bool isOdd(int x)
{
    return Derived::getInstance(x)->oddCheck();
}

bool isEven(int x)
{
    return Derived::getInstance(x)->evenCheck();
}

Dummy main in main.cpp :

#include <iostream>

#include "testedLibrary.hpp"

using namespace std;

int main()
{
    int x;
    cin >> x;

    cout << x << " is odd:" << boolalpha << isOdd(x) << endl;
    cout << x << " is even:" << boolalpha << isEven(x) << endl;

    return 0;
}

I don't use VS, but I think all it takes is to add main.cpp testedLibraryImpl.cpp and libraryToBeMockedImpl.cpp to the project. I would rather use gcc:

g++ main.cpp libraryToBeMockedImpl.cpp testedLibraryImpl.cpp -o production

Well, let's start with my answer. I would create header file 'libraryMockSeam.hpp':

#pragma once

#include "libraryToBeMocked.hpp"

class Provider
{
public:
    virtual Base* getInstance() = 0;
    virtual Base* getInstance(int x) = 0;
};

Provider* registerNewMockProvider(Provider*);

and libraryMockSeam.cpp :

#include "libraryMockSeam.hpp"

namespace
{
Provider* currentProvider = 0;
}

Provider* registerNewMockProvider(Provider* newProvider)
{
    Provider* t = currentProvider;
    currentProvider = newProvider;
    return t;
}

Base* Derived::getInstance()
{
    return currentProvider->getInstance();
}

Base* Derived::getInstance(int x)
{
    return currentProvider->getInstance(x);
}

As you can see this source file provides it's own implementation of getInstance methods. It delegates it to the registered Provider. The headers introduced the Provider interface and a function that allows to register your provider. That's it.

Let's look at the test:

#include "testedLibrary.hpp"
#include "libraryMockSeam.hpp"

#include <gtest/gtest.h>
#include <gmock/gmock.h>

using namespace ::testing;

class MockProvider : public Provider
{
public:
 MOCK_METHOD1(getInstance, Base*(int x));
 MOCK_METHOD0(getInstance, Base*());
};

class MyMock : public Base
{
public:
 MOCK_METHOD0(evenCheck, bool());
 MOCK_METHOD0(oddCheck, bool());
};

class MyTestSuite : public Test
{
public:
    MyTestSuite()
    {
        registerNewMockProvider(&provider);
    }

    StrictMock<MyMock> mock;
    StrictMock<MockProvider> provider;
};

TEST_F(MyTestSuite, checksForOddValue)
{
    EXPECT_CALL(provider, getInstance(1)).Times(2).WillRepeatedly(Return(&mock));

    EXPECT_CALL(mock, evenCheck()).WillOnce(Return(false));
    EXPECT_CALL(mock, oddCheck()).WillOnce(Return(true));

    EXPECT_TRUE(isOdd(1));
    EXPECT_FALSE(isEven(1));
}

TEST_F(MyTestSuite, checksForEvenValue)
{
    EXPECT_CALL(provider, getInstance(1)).Times(2).WillRepeatedly(Return(&mock));

    EXPECT_CALL(mock, evenCheck()).WillOnce(Return(true));
    EXPECT_CALL(mock, oddCheck()).WillOnce(Return(false));

    EXPECT_TRUE(isEven(1));
    EXPECT_FALSE(isOdd(1));
}

I would compile it like that:

g++ test.cpp testedLibraryImpl.cpp libraryMockSeam.cpp -lgmock -lgmock_main -lpthread -o test

You should notice that I did not provide libraryToBeMocked.cpp in this case, the implementation is already provided by libraryMockSeam.cpp . In case you would like to use whole libraries (that is *.lib files(or *.a )) You would be able to provide both mocked library and the seam but later one should be provided to the linker before the library.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM