Gmock
GMock provides the ability to mock implementations. For example let's say that you are testing some logic which performs some operation on data received fromm a database. To keep tests fast, and the logic verifiable, it's probably useful to have a mock database which you can control. What we can do is create a database gateway: an interface that abstracts away the concrete database implementation. Then we can have a concrete production class, and a second concrete mock class acts like a real database. Gmock makes developing such a class much easier by providing macros to generate method implementations and assert passed parameters and calling conditions (ex. how many times the function is called, when it's called, etc.).
The googletest github repo contains both gtest and gmock. So the only thing we need to do to use gmock is add gmock to the target_link_libraries
call for our test in CMake.
target_link_libraries(Test PRIVATE GTest::gtest_main PRIVATE GTest::gmock)
Then in our test file we can include <gmock/gmock.h>
.
We can mock methods of a class (we cannot mock free functions) with the MOCK_METHOD
macro which takes the method return type, name, arguments surrounded by parentheses,
and optionally and qualifiers surrounded by parentheses such as const
, noexcept
, and override
.
Ref qualifiers and calling convention can also be specified in this parameter by surrounding the qualifier in ref()
and Calltype()
, respectively. Mock methods must always be public.
class MyMock {
public:
MOCK_METHOD(std::string, getName, (), (const));
// std::string getName() const;
MOCK_METHOD(int, mult, (int, int), (const, noexcept));
// int mult(int, int) const;
MOCK_METHOD(void, setAge, (int));
// void setAge(int);
};
In a test, we can use the EXPECT_CALL
macro to assert that a method gets called, use matchers to assert conditions about the passed arguments, and use actions and cardinalities to
control the behavior of the mocked method. The first argument to EXPECT_CALL
is an instance of a mock class, the second argument is the name of the method that should be called and matchers surrounded by parentheses.
The nth matcher in the parentheses applies to the nth argument of the method call.
We discussed matchers in the last chapter, but one matcher we haven't discussed is testing::_
which is a matcher that matches anything.
using namespace testing;
TEST(MockTest, sqrtCalled) {
MyMock mock;
EXPECT_CALL(mock, mult(_, AnyOf(Eq(10), Gt(100))));
// _ indicates the first argument can be anything
mock.mult(2, 300); // passes because the second argument is > 100
}
Actions
This is great to check that a function is called, but let's make the mock method do something.
We can specify an action to WillOnce
or WillRepeatedly
by chaining these function calls after the EXPECT_CALL
macro.
EXPECT_CALL(mock, mult(_, AnyOf(Eq(10), Gt(100))))
.WillOnce(/* action 1*/)
.WillOnce(/* action 2*/)
.WillOnce(/* action 3*/)
.WillRepeatedly(/* action 4 */);
// let n be the number of times mock.mult is called
// if n <= 3, do action_n
// else do action_4
Return([optional value])
- returns from a void function or returns the passed valueReturnArg<N>()
- returns the nth argument to the function (0 based)ReturnRoundRobin(vectorOrInitializerList)
- circles through elements in the container, returning each one and moving to the nextReturnRef(variable)
- returns reference tovariable
Throw(exn)
- throwsexn
Assign(&variable, value)
- assignsvalue
tovariable
. Expects a pointer as the first argumentSaveArg<N>(pointer)
- saves the nth argument to the value pointed to bypointer
.DeleteArg<N>()
- deletes the nth argument, which should be a pointerSetArgReferee<N>(value)
- assigns the nth argument tovalue
. The arg should be a referenceInvoke(f)
- calls the callable objectf
with the parameters passed to the functionInvoke(objectPointer, member function pointer)
InvokeWithoutArgs(f)
- invokes a function taking no argumentsInvokeArgument<N>(args...)
- invokes the nth argument as a callable object passing inargs...
We can also compose actions as well:
DoAll(actions...)
- self-explanatoryIgnoreResult(action)
- ignore the result of an actionWithArg<N>(action)
- executionsaction
with the nth argument passedWithArgs<N0, N1, N...>(action)
More information is available here.
So let's mock our class:
TEST(MockTest, testActions) {
MyMock mock;
EXPECT_CALL(mock, mult(_, AnyOf(Eq(10), Gt(100))))
.WillRepeatedly(Invoke([](auto a, auto b) { return a * b; }));
// we don't actually need to wrap the callable object in Invoke()
const std::initializer_list<std::string> names = { "Molly", "Andrew", "Jimmy", "Julia", "Kathy", "Roger" };
EXPECT_CALL(mock, getName())
.WillRepeatedly(ReturnRoundRobin<std::string>(names));
int prevAge = 0;
EXPECT_CALL(mock, setAge(Gt<int&>(prevAge))) // specify that prevAge is passed by reference, could also pass std::ref(prevAge)
.WillRepeatedly(SaveArg<0>(&prevAge));
ASSERT_EQ(mock.mult(2, 300), 2 * 300);
mock.setAge(1);
mock.setAge(2);
mock.setAge(4);
mock.setAge(10);
// mock.setAge(8); // error, 8 is < prevAge
ASSERT_THAT(std::vector<std::string> { mock.getName() }, IsSubsetOf(names));
}
Cardinalities and Sequences
Without WillOnce
or WillRepeatedly
, the test will fail if the called function is called more than once.
This is because, by default, the expectation has a cardinality (amount of times the function can be executed) of 1
.
We can change this behavior by the Times()
member function and passing it a cardinality such as:
AnyNumber()
AtLeast(n)
AtMost(n)
Between(n, m)
Exactly(n)
or just a numbern
directly
By default, each usage of WillOnce
increments the expected call count by 1
(starting from 0
), and the presence of WillRepeatedly
allows the function to be called any number of times.
TEST(MockTest, testCardinalities) {
MyMock mock;
EXPECT_CALL(mock, mult(_, _))
.Times(2);
EXPECT_CALL(mock, getName())
.Times(Between(1, 10));
mock.getName();
mock.mult(1, 1);
mock.mult(0, 0);
}
If you want to enforce that calls occur in a certain order (say that mult
must be called before getName
),
we can use an InSequence
RAII object to enforce that functions are called in the order the EXPECT_CALLS
are called.
All invocations of the gmock macros that occur while an InSequence
object is alive synchronizes the order of these expectations.
TEST(MockTest, testCardinalities) {
MyMock mock;
{
InSequence seq;
EXPECT_CALL(mock, mult(_, _))
.Times(2);
EXPECT_CALL(mock, getName())
.Times(Between(1, 10));
}
EXPECT_CALL(mock, setAge(_))
.Times(AnyNumber());
mock.setAge(10);
// set age not synchronized in block with InSequence object
// can occur in any order
mock.mult(1, 1);
mock.mult(0, 0);
// enforces that calls to mult must occur before calls
// to getName
mock.setAge(20);
mock.getName();
}
For more flexible sequences, gmock provides the InSequence()
and After()
functions.
This works by constructing a DAG and enforcing the functions are called in topological order.
We can use the InSequence()
function and pass in InSequence
objects to make that mock part of the specified sequences. Mocks in the same sequence must be called in the order they are defined.
using ::testing::Sequence;
...
Sequence s1, s2;
EXPECT_CALL(foo, A())
.InSequence(s1, s2);
// foo(A()) is in sequence s1 and s2
EXPECT_CALL(bar, B())
.InSequence(s1);
// bar(B()) in sequence s1, must occur after foo(A())
EXPECT_CALL(bar, C())
.InSequence(s2);
// bar(C()) in sequence s2, must occur after foo(A())
EXPECT_CALL(foo, D())
.InSequence(s2);
// foo(D()) in sequence s2, must occur after foo(D())
This creates the following DAG:
+---> B
|
A ---|
|
+---> C ---> D
Above example taken from gmock reference
If you don't want to set any expectations about a function being called, you can use the ON_CALL
macro instead of EXPECT_CALL
.
It works exactly the same expect ON_CALL
simply defines the action a function takes without adding test expectations.
ON_CALL
should be preferred whenever you don't want to enforce call expectations (such as the number of times a function is called).
Misc
We can use ON_CALL
and WillByDefault
to specify different actions for when different parameters to a function are used.
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Gt;
using ::testing::Return;
...
ON_CALL(foo, Sign(_))
.WillByDefault(Return(-1));
ON_CALL(foo, Sign(0))
.WillByDefault(Return(0));
ON_CALL(foo, Sign(Gt(0)))
.WillByDefault(Return(1));
EXPECT_CALL(foo, Sign(_))
.Times(AnyNumber());
foo.Sign(5); // This should return 1.
foo.Sign(-9); // This should return -1.
foo.Sign(0); // This should return 0.
Google test provides a gmock cheat sheet, gmock cookbook, and gmock reference along with other documentation. There's also the gtest primer, testing reference and gtest advanced reference.
Easy on the Mocking
The key goal of mocking is to keep tests independent and fast. When unit testing, you don't need to mock every single other class or module that the UUT (unit under test) depends on. In fact, most dependencies probably don't require mocks. An example of a good mock is mocking a filesystem. Changes to the filesystem made by one test might affect the behavior of another test. The order that tests are run in must not matter (the order is indeterminate in gtest, but this is a good engineering practice). Likewise, the filesystem is much slower than memory, so using a mock could sufficiently improve run time for tests. Another good example is that during development of module A, you might find (or already know) that some behavior module A depends on could be factored out into a separate, public, module B. Mocking can allow you to test module A without an implementation of module B's interface.
As I mention before, ON_CALL
should be preferred to EXPECT_CALL
.
This is because EXPECT_CALL
adds constraints to how the function is called, which may be an implementation detail.
Does the spec on the interface for addProductToCart()
say that doFoo()
is called 3 times after makeFoo()
is called? If not, ON_CALL
is probably a better choice than EXPECT_CALL
.
The death of testing is when the test suite makes it difficult to refactor or alter the production code in any way. Overly mocking can cause this.