Search code examples
c++googlemock

How to return a specific value every nth time of a consecutive call with Gmock


In the code Google Mock test snippet there is an EXPECT_CALL that returns True and an argument reference for 200 times.

How can I let the test only return True every nth time. For example return True each 10th call and otherwise return False.

class MockHandler : public Handler
{
    public:
        MOCK_METHOD1(RxMsg, bool(Msg &msg));
}

TEST(TestDispatcher, HandlePing)
{
    auto mockedHandler = make_unique<MockHandler>();
    Msg rxMsg = { REQUEST::REQ_PING, sizeof(DefaultMsg_t), rxMsg.rx,(uint8_t*)"0"};

    EXPECT_CALL(*mockedHandler, 
        RxMsg(_)).Times(checkValue).WillRepeatedly(
            DoAll(SetArgReferee<0>(rxMsg), Return(TRUE)));

    Dispatcher dispatcher(10, mockedHandler);

    for (int i = 0; i < 199; i++)
    {
        dispatcher.RunToCompletion();
    }
}

Solution

  • There are few approaches that might work for you. I like the solution with Invoke as a default action, because it is the most flexible. You didn't provide mcve in your question, so I wrote very simple implementations for the classes you use. Also you made a mistake using unique_ptr for the mock. In 99% of cases is must be shared_ptr, because you are sharing it between testing environment and your System Under Test.

    class Msg {};
    
    class Handler {
    public:
        virtual bool RxMsg(Msg &msg) = 0;
    };
    
    class MockHandler: public Handler
    {
    public:
        MOCK_METHOD1(RxMsg, bool(Msg &msg));
    };
    
    class Dispatcher {
    public:
        Dispatcher(std::shared_ptr<Handler> handler): h_(handler) {}
        void run() {
            Msg m;
            std::cout << h_->RxMsg(m) << std::endl;
        }
    private:
        std::shared_ptr<Handler> h_;
    };
    
    class MyFixture: public ::testing::Test {
    protected:
        MyFixture(): mockCallCounter_(0) {
            mockHandler_.reset(new MockHandler);
            sut_.reset(new Dispatcher(mockHandler_));
        }
        void configureMock(int period) {
            ON_CALL(*mockHandler_, RxMsg(_)).WillByDefault(Invoke(
                [this, period](Msg &msg) {
                    // you can also set the output arg here
                    // msg = something;
                    if ((mockCallCounter_++ % period) == 0) {
                        return true;
                    }
                    return false;
                }));
        }
        int mockCallCounter_;
        std::shared_ptr<MockHandler> mockHandler_;
        std::unique_ptr<Dispatcher> sut_;
    };
    
    TEST_F(MyFixture, HandlePing) {
        configureMock(10);
        for (int i = 0; i < 199; i++) {
            sut_->run();
        }
    }
    

    At the beginning of each test you should call configureMock method that will Invoke ON_CALL macro setting the default action for your mock. Function passed to Invoke can be any function matching the signature of the method you are overwriting. In this case it;s a function that counts how many times mock has already been called and returns appropriate value. You can also assign some particular object to the msg output argument.