Przejdź do głównej zawartości

Piotr's Less Obvious Advice on Google Mock: State maintenance

Google Mock provides several ways to maintain state inside mock objects. One way of implementing state maintenance is with SaveArg. Consider the following example.

We have a class Configurator, which allows a caller to set and get values of a parameter:

class Configurator

    virtual ~Configurator() {}

    virtual void setParamX(int n) = 0;
    virtual int getParamX() = 0;

And we have a class Client that calls Configurator's methods and it also has a method incParamXBy, that can be used to increase the current value of paramX by a certain value.

class Client

    Client(Configurator & cfg);
    virtual ~Client() {}

    void setParamX(int n);
    void incParamXBy(int n);
    int getParamX();


    Configurator & _cfg;

incParamXBy internally calls setParamX and getParamX on Configurator:

void Client::incParamXBy(int n)
    _cfg.setParamX(_cfg.getParamX() + n);

Let's assume that the initial value of paramX is A and that we want to increase paramX by B each time we call incParamXBy. Our expectation is that if incParamXBy is called for the first time, it will result in calling cfg.setParamX(A+B).
  • second call of incParamXBy(B) will result in calling cfg.setPAramX(A + 2*B)
  • third call: cfg.setPAramX(A + 3*B), and so on...

Interaction between Client and Configurator in this case is such that Client relies on Configurator (issues getParamX()) in order to correctly calculate the parameter for next setParamX() call. If we want to test Client object, we need a MockConfigurator that is able to remember its current value of the parameter we are increasing:

class MockConfigurator : public Configurator

    int paramX;
    int * paramX_ptr;

        paramX = 0;
        paramX_ptr = &paramX;
    MOCK_METHOD1(setParamX, void(int n));
    MOCK_METHOD0(getParamX, int());

MockConfigurator has a public member paramX. We will use paramX to store current value of the parameter. We could have put paramX outside of MockConfigurator, in the test code, but I think it makes more sense to bind the parameter with the object.

And now the test:

MockConfigurator cfg;
Client client(cfg);

int inc_value = 10;

//getParamX will be called a number of times.
//If it is called, we will return the value pointed to by paramX_ptr.
//Returning with ReturnPointee is necessary, since we need to have
//the actual (updated) value each time the method is called.

EXPECT_CALL(cfg, getParamX())

//SaveArg stores the 0th parameter of the call in the value pointed to by paramX_ptr (paramX)
//expectation 3
EXPECT_CALL(cfg, setParamX(cfg.paramX + 3*inc_value))
    .WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));
//expectation 2
EXPECT_CALL(cfg, setParamX(cfg.paramX + 2*inc_value))
    .WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));
//expectation 1
EXPECT_CALL(cfg, setParamX(cfg.paramX + inc_value))
    .WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));

client.incParamXBy(inc_value); //this will match expectation 1
//this will match expectation 2
client.incParamXBy(inc_value); //this will match expectation 3

Is it the only way to maintain state inside a mock object? Of course not. First of all, we could have an equivalent test without maintaining state inside out mock object at all. We could have written all method calls and all expectations using pre-calculated values that we know validate Client's behaviour. The point is that state maintenance gives us more flexibility that may be advantageous in case we want to write a number of similar tests (e.g. test the same behaviour for a range of parameter values or test different behaviours that rely accessing value previously stored in a mock).

Secondly, we can always implement state maintenance by using Invoke. Calling another function or method allows us not only to an equivalent to SaveArg, but also more complex state maintenance cases.


Popularne posty z tego bloga

Unit Testing code with IO file operations (in Python)

We may often come across a piece of code that was written without Unit Tests at all. In addition, the piece of code may be dealing with IO like file writing and reading, which makes it more difficult to Unit Test it when we are trying to refactor and modify. Let's suppose the code in question looks like this:

def writeInitialsToFile(filename, name, surname):
    initials = name[0] + '.' + surname[0] + '.'
    with open(filename, 'w') as file:

def readInitials(filename):
    initials = None
    with open(filename, 'r') as file:
        initials = file.readline()
    return initials

A straightforward and bad idea would be to write a couple of Unit Tests that make use of a real file and simply test the reading and writing. Is therea a better way to test this code?

First of all, we need a way to replace the real file with something else. For both reading and writing we will now have a couple of functions, one that expects a stream fo…

Piotr's Less Obvious Advice on Google Mock: Returning new objects from a mock

Google Mock provides a way to return newly created objects from a mock method. Suppose we have a  Generator class that is supposed to generate new objects when createNewRecord method is called:

class Generator
    virtual ~Generator() {}
    virtual Record * createNewRecord() = 0;

...and suppose we want to mock this class:

class MockGenerator : public Generator
    MOCK_METHOD0(createNewRecord, Record * ());

Suppose the caller class Client has run method defined as follows:

void Client::run()
    for(int i = 0; i < 3; i++)
        rec_tab[i] = gen.createNewRecord();

We want the mock to return a pointer to a new object each time createNewRecord is called. This is how we can express this in the test code:

TEST(ClientTest, CanRun)
    MockGenerator gen;
    Client c(gen);

    EXPECT_CALL(gen, createNewRecord())
                 //this is equivalent of returning new Record(1,2,3)