Article summary
When working with the GoogleMock C++ mocking library, you can get pretty far using only default expectation return values or explicitly specifying expectation return values. There are some additional options that can save you a lot of effort in some circumstances though. Let’s take a look at a few of them.
Standard Values
Consider an interface and mock like the following:
class Timer {
public:
Timer() {}
virtual ~Timer() {}
virtual void Start(int) = 0;
virtual void Stop() = 0;
virtual bool IsActive() = 0;
virtual QString GetId() = 0;
};
class MockTimer: public Timer {
public:
MOCK_METHOD1(Start, void(int));
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(IsActive, bool());
MOCK_METHOD0(GetId, QString());
};
You can set up an expectation for the IsActive() call using:
TEST(MockTest, TestDefaultValue)
{
MockTimer timer;
EXPECT_CALL(timer, IsActive()).Times(AnyNumber());
qDebug() << "IsActive value: " << timer.IsActive();
}
And the test will run cleanly with the mock answering the default return value (false in this case). But what about the GetId() call?
Default Values for Custom Types
If we try to set a similar expectation for GetId() and execute the test, we'll see an error:
: Failure
The mock function has no default action set, and its return type has no default value set.
The issue here is that GoogleMock doesn't know what the appropriate default value should be for a QString. We can certainly provide one:
TEST(MockTest2, TestDefaultValue)
{
MockTimer timer;
EXPECT_CALL(timer, GetId()).WillRepeatedly(Return(QString("someDefaultValue")));
qDebug() << "Timer GetId value: " << timer.GetId();
}
This may be painful to do, though, in the case where you have a number of mocks with several methods whose return values are unimportant but of unknown types. What we can do instead is set up a default return value for a custom type:
TEST(MockTest2, TestDefaultValue)
{
::testing::DefaultValue::Set(QString("someDefaultValue"));
MockTimer timer;
EXPECT_CALL(timer, GetId()).Times(AnyNumber());
qDebug() << "Timer GetId value: " << timer.GetId();
}
And now we can set expectations on methods that return QStrings without having to explicitly specify return values. Note that the call to DefaultValue::Set must come before your mocks are instantiated for the default value to be respected.
Dynamic Return Values
Let's take another look our Timer interface. The expectation is that our Timer.IsActive() call returns true between Start() and Stop() calls. How can we specify this in our test?
If the expected call sequence is known, we can set things up as follows:
TEST(MockTest3, TestIsActiveValue)
{
MockTimer timer;
EXPECT_CALL(timer, IsActive()).WillOnce(Return(false)).WillOnce(Return(true)).WillRepeatedly(Return(false));
qDebug() << "Timer IsActive value1: " << timer.IsActive();
qDebug() << "Timer IsActive value2: " << timer.IsActive();
qDebug() << "Timer IsActive value3: " << timer.IsActive();
}
Or perhaps use a sequence of expected calls:
TEST(MockTest4, TestIsActiveValueSequence)
{
MockTimer timer;
InSequence s;
EXPECT_CALL(timer, IsActive()).WillOnce(Return(false));
EXPECT_CALL(timer, Start(_));
EXPECT_CALL(timer, IsActive()).WillOnce(Return(true));
EXPECT_CALL(timer, Stop());
EXPECT_CALL(timer, IsActive()).WillRepeatedly(Return(false));
qDebug() << "Timer IsActive value1: " << timer.IsActive();
timer.Start(1000);
qDebug() << "Timer IsActive value2: " << timer.IsActive();
timer.Stop();
qDebug() << "Timer IsActive value3: " << timer.IsActive();
}
But this maybe over-specifying the behavior. You may simply want IsActive() to return the right values based on the Start() and Stop() calls without having to worry about the order of the calls. To enable this behavior, we can use the ReturnPointee and Assign actions. Here's an example:
TEST(MockTest4, TestIsActiveValueUsingVariable)
{
MockTimer timer;
bool timerActive = false;
EXPECT_CALL(timer, IsActive()).WillRepeatedly(ReturnPointee(&timerActive));
EXPECT_CALL(timer, Start(_)).WillRepeatedly(Assign(&timerActive, true));
EXPECT_CALL(timer, Stop()).WillRepeatedly(Assign(&timerActive, false));
qDebug() << "Timer IsActive value1: " << timer.IsActive();
timer.Start(1000);
qDebug() << "Timer IsActive value2: " << timer.IsActive();
timer.Stop();
qDebug() << "Timer IsActive value3: " << timer.IsActive();
}
Now IsActive() will return the state of our variable "timerActive", and this value will be changed each time Start() or Stop() is called. This pattern can come up fairly frequently when an object has methods to toggle state and accessors which need to return the current value.
Hopefully these tips will help you clean up your tests a bit!