Search code examples
c++googletestgooglemock

Change return value of a mock function when another mock function is called


Suppose I have the following mock

enum class State
{
    IDLE,
    BUSY,
    ERROR1,
    ERROR2
};

class MockActuator : public ActuatorInterface
{
    public:
        MOCK_METHOD0(doAction, void());
        MOCK_METHOD0(getState, State());
};

The module I am testing makes the assumption that if doAction() is called, getState() should return BUSY.

How do I encode this assumption under GMock? I would like to keep getState() as a mocked function as there are other return values that I need to test.

My first attempt is the following:

    EXPECT_CALL(actuator, doAction()).Times(1).WillOnce(InvokeWithoutArgs(
                [&](){
                    ON_CALL(actuator, getState()).WillByDefault(Return(State::BUSY));
                }));

But that gives the following cryptic error:

/usr/src/googletest/googlemock/include/gmock/gmock-actions.h:861:64: error: void value not ignored as it ought to be
   Result Perform(const ArgumentTuple&) { return function_impl_(); }

Solution

  • You can see an example of how to do this in the docs.

    Following your classes.

    First a fake class that mimics the original:

    enum class State
    {
        IDLE,
        BUSY,
        ERROR1,
        ERROR2
    };
    
    class FakeActuator : public ActuatorInterface
    {
        public:
            virtual void doAction () { _state = BUSY; }
            virtual State getState () { return _state; }
        private:
            State _state;
    };
    
    

    After that, the mock class:

    class MockActuator : public ActuatorInterface {
     public:
      // Normal mock method definitions using gMock.
      MOCK_METHOD(void, doAction, (), (override));
      MOCK_METHOD(State, getState, (), (override));
    
      // Delegates the default actions of the methods to a FakeActuator object.
      // This must be called *before* the custom ON_CALL() statements.
      void DelegateToFake() {
        ON_CALL(*this, doAction).WillByDefault([this]() {
          fake_.doAction();
        });
        ON_CALL(*this, getState).WillByDefault([this]() {
          return fake_.getState();
        });
      }
    
     private:
      FakeActuator fake_;  // Keeps an instance of the fake in the mock.
    };
    

    It is this "fake" object that can keep state between calls. You can create as many "fake" classes as situations you need also.

    And the test:

    TEST(AbcTest, Xyz) {
      MockFoo foo;
    
      foo.DelegateToFake();  // Enables the fake for delegation.
    
      // Put your ON_CALL(foo, ...)s here, if any.
    
      // No action specified, meaning to use the default action.
      EXPECT_CALL(foo, doAction());
      EXPECT_CALL(foo, getState());
    
      foo.doAction();  // FakeActuator::doAction() is invoked.
    
      EXPECT_EQ(BUSY, foo.getState());  // FakeActuator::getState() is invoked.
    }