Search code examples
c++unit-testinggoogletest

How to intercept return value of a gmock action


I am testing a class that has a factory. The factory is mocked using google mock. I used action and WillByDefault() to enable the mocked factory to construct example objects and return pointers to them. In the test I ask my class to create an object using the mocked factory and to return pointer to crated object. I want to make sure pointer returned from class is the same as pointer returned by mocked factory to the class.

Lets say I have a class dog, a dog factory and a kennel that uses the factory.

class dog;

class dogFactory{
    dog* makeDog() { return new dog; }
}

ACTION(makeDogInMock){ return new dog; }

class factoryMock : public factory{
    MOCK_METHOD1(makeDog, dog*());
}

class kennel{
    public:
    dog* dogInKennel;
    dogFactory factory;

    void putDogInKennel(){
        this->dogInKennel = factory.makeDog();
    }

    dog* getDogInKennel(){
        return this->dogInKennel;
    }

}

Then in test constructor I use the action as default operation:

ON_CALL(factoryMock, dog(_)).WillByDefault(makeDogInMock)

In test I want to know whether the kennel returns the same pointer as the dogFactory returned when called in putDogInKennel(), but I have no idea how to achieve this.


Solution

  • In your example it will never be the dog you specified in the default action, because the design of your system is incorrect (if you want to test it with the factory mock of course). Inside your system (kennel) you shouldn't keep the concrete factory object, but the pointer to the factory interface. This is called Dependency Injection. Your example might be modified in the following way:

    class Dog{};
    
    class IDogFactory {
    public:
        virtual ~IDogFactory() = default;
        virtual Dog* makeDog() = 0;
    };
    
    class DogFactory: public IDogFactory {
    public:
        ~DogFactory() override = default;
        Dog* makeDog() override {
            return new Dog;
        }
    };
    
    class DogFactoryMock: public IDogFactory {
    public:
        MOCK_METHOD0(makeDog, Dog*());
    };
    
    class Kennel {
    public:
        explicit Kennel(IDogFactory* factory): factory_(factory) {}
        void putDogInKennel(){
            dogInKennel_ = factory_->makeDog();
        }
        Dog* getDogInKennel(){
            return dogInKennel_;
        }
    private:
        Dog* dogInKennel_;
        IDogFactory* factory_;
    };
    
    using testing::Return;
    
    TEST(xxx, yyy) {
        Dog d;
        DogFactoryMock factoryMock;
        ON_CALL(factoryMock, makeDog()).WillByDefault(Return(&d));
        Kennel sut(&factoryMock);
        sut.putDogInKennel();
        Dog* dPtr = sut.getDogInKennel();
        std::cout << (dPtr == &d) << std::endl;  // outputs "true"
    }
    

    IDogFactory is the interface to the factory that is able to create the object of Dog. How exactly this Dog creation is done depends on the factory implementation, but your system does not depend on it. So, in the production you might create your Kennel passing the pointer to DogFactory, while in testing you can pass a pointer to DogFactoryMock. In the example I used raw pointers to objects on the stack, but I recommend using std::shared_ptr instead. Both for the return type from the factory and for the factory pointer inside your system.