Monday, February 25, 2013

Piotr's Less Obvious Advice on Google Mock: Assigning values through method calls

On many occasions, we may want to test a real object by passing to it prepared buffers of data (characters, bytes, etc.) that are filled in when the object calls a mock method. This can be achieved by using SetArrayArg.

Suppose we have a Client object that implements a find method, which takes an array of characters as input argument. The method queries a Driver object for chunks of data (other arrays of characters of the same size as the input argument) and compares each chunk with the input array:

void Client::find(char * buf)
{
    char tmp_buf[buflen];
    bool found = false;

    while(!found && _drv.getDataChunk(tmp_buf))
    {
        int i = 0;
        while(i < buflen && buf[i] == tmp_buf[i])
        {
            i++;
        }

        if(i == buflen)
            found = true;
    }

    if(found)
        cout << "Found matching chunk of data." << endl;
    else
        cout << "No matching chunk of data found." << endl;
}

Client assumes that Diver implements the following contract:
  • if there is no more data chunks, getDataChunk will return false,
  • otherwise, it will return true and will fill in the buffer passed to it through pointer as input, with next chunk of data.
class Driver
{
    public:

    virtual ~Driver() {}

    virtual bool getDataChunk(char *) = 0;
};

class MockDriver : public Driver
{
    public:

    MOCK_METHOD1(getDataChunk, bool(char *));
};

Let's now consider a test in which we want to simulate that a matching chunk of data is found on the first call of getDataChunk.

TEST(ClientTest, FindsAMatchOnFirstCall)
{
    MockDriver drv;
    Client client(drv);

        //this is the buffer that will be searched for
    char buf [] = {'a','a','a','a','a','a','a','a'};

        //this is the matching buffer that will be returned by MockDriver
    char bufA [] = {'a','a','a','a','a','a','a','a'};

    EXPECT_CALL(drv, getDataChunk(_))
        .Times(1) //we are expecting just one call, since the first call will return the matching buffer

                //the mock will return true and before doing so it will copy the contents of bufA into the table
                //pointed to by the pointer passed to getDataChunk method as the input parameter
        .WillOnce(DoAll(SetArrayArgument<0>(bufA, bufA+8), Return(true)));
   
    client.find(buf);
}


Exercise: try to modify the code above to validate your expectations on Client::find method when there is no matching chunk of data.

See also