Google Mock
As the name suggests, Google Mock is a mock framework. With the help of the framework, it
is easy to check whether functions are called as expected and to define what a function
that is yet to be implemented should return. Integration and build work analogously to
the already presented framework Google Test.
Different types of tests are to be distinguished here. As examples unit tests for testing
a component or integration tests, which examine the interaction of several components,
are called. For most programming languages, there are also frameworks that significantly
facilitate the creation and execution, including for C++.
General
The use of Google Mock is also possible with other test frameworks in addition to Google
Test, but the aforementioned integration is then more complex. Therefore, the following
explanations refer to the joint use with Google Test.
First of all, the header must be supplemented for this purpose. In addition, it makes
sense to make the namespace "testing" visible:
# include <gtest/gtest.h>
# include <gmock/gmock.h>
using namespace testing ;
In order to understand how Google Mock works, it is necessary to first deal with the
concept of mock. A mock object implements the same interface as the real object.
However, the main difference is that a mock object receives information at runtime about
what calls are expected on it and how it should react.
Creation of a mock object
For mocking functions the macros MOCK_METHOD are needed. How these are to be implemented
is explained below with an example.
The example is an application that is supposed to display a list of users. This has been
created according to the common Model View Controller (MVC) concept and contains a
UserListController. This controller is the function that is to be tested. The UI that is
set on this controller should react to certain situations.
Once user loading is complete, the UI must be updated. If a user has been deleted, it
must be removed from the list. Via the controller, a listener can be registered for this
purpose. The controller also needs information such as the current account for which it
should load the list of users. A DataProvider is injected into the controller for this
purpose.
The two interfaces should be defined as follows:
UserListControllerListener
class UserListControllerListener {
public :
virtual void onRefreshData () = 0;
virtual void onRemoveItem (int position ) = 0;
};
UserListControllerDataProvider
class UserListControllerDataProvider {
private :
virtual long getAccountId () = 0;
};
Now the two mocks are defined, which should make it possible to test the behavior of the
UserListController interfaces. For this purpose, a new class is created that contains
the mock implementation.
UserListControllerListenerMock
class UserListControllerListenerMock : public UserListControllerListener
{
public :
MOCK_METHOD0 ( onRefreshData , void ());
MOCK_METHOD1 ( onRemoveItem , void (int));
};
UserListControllerDataProviderMock
class UserListControllerDataProviderMock : public
UserListControllerDataProvider {
public :
MOCK_METHOD0 ( getAccountId , long ());
};
As can be seen, the mock is implemented in the header, no further code in a cpp is needed
for this. However, other things that are less obvious must also be considered. The mock
implementation inherits from the UserListControllerDataProviderMock class to be mocked :
public UserListControllerDataProvider. The inheritance must be public, otherwise
compilation errors may occur. The same applies to the visibility within the class. In
the UserListControllerDataProvider the function getAccountId is marked as private but in
the associated mock as public. This circumstance is unproblematic in C++, however, since
when overwriting a function its visibility can be changed. It is therefore possible to
define functions in the code with arbitrary visibility. It must only be paid attention
with the implementation of the Mocks to mark these there as public.
The macros themselves have a number at the end e.g. MOCK_METHOD0. The number corresponds
to the number of parameters that the function to be mocked has. Again, you get a cryptic
error message when compiling if the numbers do not match.
It should also be mentioned that it does not matter for the implementation of the mock
whether the functions are purely virtual or not. However, for non-virtual functions the
implementation changes slightly.
If you want to save time and don't want to write the mock classes by hand, you can use a
Python script provided by Google that can generate the mock implementation from a
header. Especially for smaller interfaces, however, the time required should be
weighed.
Expectations
For tests that use mocks, it is necessary to define what is expected from each mock.
However, it is essential to have the right expectations. If these are too strict, the
tests may fail even for changes that have nothing to do with the test in question.
However, if expectations are set too loosely, possible errors in the code may possibly
be overlooked.
There are several configuration options available for creating Expectations that can be
done on a mock:
- Cardinalities
How often should the function be called?
- Matchers
What arguments are expected?
- Actions
How should the mock implementation respond to the call?
The terms Matchers, Cardinalities and Actions are given by Google.
Expectations with Cardinalities
A simple expectation is to be considered on the basis of the example described above. It
is tested whether the onRefreshData is called. It is assumed that the instance of the
UserListControllerListenerMock has already been created and registered on the
UserListController as a listener. The instance of the mock object is the mockListener.
The defined Expectation is as follows:
EXPECT_CALL ( mockListener , onRefreshData ()). Times (1);
mUserListControler -> loadData ();
The mock listener is told that the function onRefreshData should be called exactly once.
If it is called twice or not at all, the test fails. First, the mock must be configured
before the call that uses the mock object is executed.
The cardinality can be specified as a direct number. In this case the function is
expected to be called exactly once. Zero is also a correct specification. This checks
that the function is not called. It is also possible to specify a minimum number:
EXPECT_CALL ( mockListener , onRefreshData ()). Times ( AtLeast (1));
In the above example, the function must be called at least once for the test to be
satisfied. However, it is also successful if it is called more than once.
Expectations with Matchers
The following example checks the arguments of the call:
EXPECT_CALL ( mockListener , onRemoveItem (Eq (5)). Times (1) ;
It is to be tested whether the function onRemoveItem is called with the argument 5. Eq is
a matcher that already appears in the section Assertion with Matchers (part 1 of the
article). So it is possible to perform a whole range of different checks. Matchers are
available for many mathematical comparisons (e.g. Ge() is ≥). The existence of objects
can be checked with the matchers IsNull() and IsNotNull(). The parameter "_" stands for
the wildcard Any. If it is passed instead of Eq(5), the call is valid with any
parameter.
Expectations with Actions
Actions can be used to pass specific behaviors to a mock. A simple example of this is the
definition of a return value.
EXPECT_CALL ( mockDataProvider , getAccountId (). WillOnce ( Return (17) );
The value 17 is returned during the call of the function. All functions can be used in an
EXPECT_CALL. There is also no explicit cardinality given. Google Mock can calculate the
cardinality based on the calls of WillOnce.
Multiple Expectations in one Test
It is possible that more than one expectation is included in an integration test. If
several Expectations are applied within a function, the order of processing or
evaluation is also relevant. Expectations are evaluated in the reverse order of their
definition. For the action and cardinality, the first expectation whose matcher fits is
always used.
Example for the evaluation of Expectations
Multiple Expectations
EXPECT_CALL ( mockListener , onRemoveItem (_)). Times ( AtLeast (1));
EXPECT_CALL ( mockListener , onRemoveItem (Eq (42) )). Times (3);
Several scenarios are conceivable, resulting in different outcomes. If the function
onRemoveItem is called four times with the value 42, this test fails. For all calls with
the value 42 the lower expectation takes effect. In the test, Times (3) specifies that
the call with 42 is expected three times. However, if the function is called three times
with the value 42 without another call, the test fails. Because in the first line it is
defined that the function should be called at least once with any other value. Thus, the
test is successful if first the value 42 is called three times and then another with
75.
Summary
The explanations show that Google Test and Google Mock are powerful tools for writing
automated tests in C++. The libraries can be easily integrated into existing projects
using CMake.
Google Test offers a variety of simple ways to write assertions. Tests can be equipped
with further information through log messages and scoped traces. In addition, the
framework offers the possibility to check more complex topics such as exceptions and
aborts.
The biggest limitation of the frameworks, however, is the lack of support for tests of
asynchronous functionalities. The fact that Google Test is not considered a Threadsave
under some platforms could also pose challenges for users of these platforms.