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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.