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
{
public:
virtual ~Generator() {}
virtual Record * createNewRecord() = 0;
};
...and suppose we want to mock this class:
class MockGenerator : public Generator
{
public:
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())
.Times(3)
//this is equivalent of returning new Record(1,2,3)
.WillOnce(ReturnNew<Record>(1,2,3))
.WillOnce(ReturnNew<Record>(2,3,4))
.WillOnce(ReturnNew<Record>(3,4,5));
c.run();
}
Creating and passing new objects through a method parameter
class Generator
{
public:
virtual ~Generator() {}
virtual Record * createNewRecord() = 0;
};
...and suppose we want to mock this class:
class MockGenerator : public Generator
{
public:
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())
.Times(3)
//this is equivalent of returning new Record(1,2,3)
.WillOnce(ReturnNew<Record>(1,2,3))
.WillOnce(ReturnNew<Record>(2,3,4))
.WillOnce(ReturnNew<Record>(3,4,5));
c.run();
}
Creating and passing new objects through a method parameter
But
what if we want to return a new object through a method parameter?
Sometimes caller passes a pointer-to-pointer to a method that is
supposed to create a new object and assign its address to the method
parameter. Let's modify our example above so that:
- the caller (class Client) has a private member:
Record * rec;
- the caller (class Client) has the following method:
void Client::getNewRec()
{
// we are passing Record ** (pointer to pointer) to createNewRecord
// and Generator is supposed to allocate memory gen.createNewRecord(&rec);
}
{
// we are passing Record ** (pointer to pointer) to createNewRecord
// and Generator is supposed to allocate memory gen.createNewRecord(&rec);
}
- our Generator.h has the following declaration:
virtual void createNewRecord(Record ** rec) = 0;
- which results in the following declaration in MockGenerator.h:
MOCK_METHOD1(createNewRecord, void (Record ** rec));
TEST(ClientTest, GetsNewRec)
{
MockGenerator gen;
Client c(gen);
EXPECT_CALL(gen, createNewRecord(_))
.Times(1)
.WillOnce(Invoke(my_create));
c.getNewRec();
}
Where my_create is a small function that does the trick:
void my_create(Record ** rec)
{
*rec = new Record(1,2,3);
}
.WillOnce(CreateAndPass<Record>(1,2,3));
The CreateAndPass action must be smart enough to create an object of a certain type (Record), create it with the parameters that we specify (1,2,3) and do the proper assignment to the pointer. For a constructor that takes three parameters, as in the example above, our new action would look like this:
ACTION_TEMPLATE(CreateAndPass,
HAS_1_TEMPLATE_PARAMS(typename, T), // the type of object to be created
AND_3_VALUE_PARAMS(p0, p1, p2)) // constructor parameters
{
// we are using TR1; assign the pointer of the newly created object to dereferenced call parameter
// this is the only parameter of the call in this case, so it has the index of 0
*(::std::tr1::get<0>(args)) = new T(p0, p1, p2);
}
That's it. This example can be easily extended for different constructors that take less or more parameters. Since the action is templatized, we can use it to construct objects of any class.