2

Using google mock, how do I specify an EXPECT_CALL with a return value N times, and then a different value N+1?

The only way I can get my test to pass is if I manually specify each iteration e.g.

EXPECT_CALL(mock, Read(address)).Times(5)
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(1));

Test Description

The code under test uses a status word to determine if a loop reading data should exit. EXPECT_CALL should set an expectation to return 1 from the mocked method Read() N times, and on the Nth cycle return 0 to indicate no more data.

Unfortunately the code in question is on a separate machine but here's a representative example.

const unsigned int statusAddress = 0;
const unsigned int dataAddress   = 1;
const unsigned int maxData       = 8;

unsigned int dataBuffer[maxData] = {0};
int readIndex = 0;

// Check if data is available (read returns 1). 
unsigned int dataIsAvailable = Read(statusAddress);

// Keep reading data until no more is available or the buffer is full.
while ((dataIsAvailable == 1) && (readIndex < maxData))
{
    // Read data word.
    dataBuffer[readIndex++] = Read(dataAddress);

    // Read status to determine if more data is available.
    dataIsAvailable = Read(statusAddress);
}

The Read method is mocked.

If I add expectations in order, the latter EXPECT_CALL is used ( I presume the WillOnce overwrites WillRepeatedly in google code ). Obviously Im misunderstanding how multiple EXPECT_CALLs is ordered.

EXPECT_CALL(mock, Read(address)).Times(4).WillRepeatedly(Return(1));
EXPECT_CALL(mock, Read(address)).Times(1).WillOnce(Return(0));
Expected: to be called 4 times
 Actual: never called - unsatisfied and active

As there is no method...

WillN(Return(1), 4)

... I presume there is a less fixed way of structuring an EXPECT_CALL for more complex and/or longer sequences of data?

2 Answers 2

0

There is a simple solution to your problem. Create an instance of InSequence before your expectations. This will guarantee that the written expectations are called in the same order.

InSequence s;
EXPECT_CALL(mock, Read(address)).Times(4).WillRepeatedly(Return(1));
EXPECT_CALL(mock, Read(address)).Times(1).WillOnce(Return(0));

Furher Informations: click

Sign up to request clarification or add additional context in comments.

Comments

-1

You might want to look into the Delegating Calls to Fake pattern for this type of test.

I think if I needed to implement a suite of tests that dealt with reading various data streams, I would probably write a hybrid Mock/Fake class that allows me to control the actual stream to be read. For example:

// Example reader class to be tested
class Reader
{
public:
    static const unsigned int statusAddress = 0;
    static const unsigned int dataAddress   = 1;
    static const unsigned int maxData       = 8;

    // Returns data sequence that was read
    vector<unsigned int> ReadFromDevice()
    {
        // Stream-reading code from your example goes here
    }

    virtual unsigned int Read(unsigned int address)
    {
        // Read from address code here
    }
};

// Mock reader class with some faking ability
class MockReader : public Reader
{
public:
    // Set up normal mock definition
    MOCK_METHOD1(Read, unsigned int(unsigned int address));

    // Optionally enable use of delegation to fake method
    void EnableFakeReader()
    {
        ON_CALL(*this, Read(_))
            .WillByDefault(Invoke(this, &MockReader::FakeRead));
    }

    // Set up a fake data sequence to be returned by Read()
    void SetFakeData(const vector<unsigned int> &data)
    {
        m_fakeData = data;
        m_fakeDataIndex = 0;
        EnableFakeReader();
    }

    // Read from fake data sequence
    unsigned int FakeRead(unsigned int address)
    {
        if (address == statusAddress)
            return m_fakeDataIndex < m_fakeData.size() ? 1 : 0;
        if (address == dataAddress)
            if (m_fakeDataIndex < m_fakeData.size())
                return m_fakeData[m_fakeDataIndex++];
        return 0;
    }

    vector<unsigned int> m_fakeData;
    size_t m_fakeDataIndex = 0;
};

TEST(ReaderTests, Test1)
{
    // Set up a fake data sequence to be read
    vector<unsigned int> testData ={ 1,2,3,4,5,6,7,8 };
    MockReader mock;
    mock.SetFakeData(testData);

    // Set any desired expectations about mocked functions
    EXPECT_CALL(mock, Read(Reader::statusAddress)).Times(9);
    EXPECT_CALL(mock, Read(Reader::dataAddress)).Times(8);

    // Data read should be the same as the test data
    EXPECT_EQ(testData, mock.ReadFromDevice());
}

Note that this example does not follow the Google Mock cookbook pattern linked above. The complete cookbook pattern would have you create a FakeReader class as well as a MockReader. The MockReader would own and delegate to a private instance of the FakeReader class.

You could also consider putting the fake into a fixture class.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.