Mocking with gMock
==================
C++ is an object-oriented language. C++ objects live in a "society", they
communicate with other objects with the same or different type.
Communication:
- sending messages
- receiving responses
State-based testing (gtest)
- is good for testing how the object responds to messages
- not that good for testing when sending messages
We replace the communication partners with fake/mock objects to test
the BEHAVIOR of the object.
Problems with dependencies:
- communication may be non-deterministic (e.g. time related)
- can be flaky
- difficult/expensive to create or reproduce (e.g. database)
- slow
- hard to simulate failures
- not exists yet
Mock object allows you to check teh interaction between itself
and the user (teh code we test)
A mock class
- has the same interface then the real
- can control the behavior at run time
- verify interactions
How to create mock objects?
- By hand?
- Automatically (jMock, EasyMock using reflection)
- in C++, we have no reflection
therefore gMock is nor a transcript of jMock/EasyMock
Different design choices:
- Macros
- DSL for expectations and actions
MOCK_METHODn( name, type );
class Foo
{
virtual void DoThis() = 0;
virtual bool DoThat( int n, double x) = 0;
};
class MockFoo : pucblic Foo
{
MOCK_METHOD0( DoThis, void());
MOCK_METHOD2( DoThat, bool(int n, double x));
};
MockFoo mock_foo;
The mock interface should answer:
- Which methods were called?
- What arguments?
- How many times?
- Which order?
- What responses?
mock_foo.DoThat(n,x)
- will be called
- with a positive n
- twice
- returning true first time, returning false second time.
Described in the DSL:
MockFoo mock_foo;
EXPECT_CALL(mock_foo, DoThat( Gt(0), _ ))
.Times(2)
.WillOnce(Return(true))
.WillOnce(Return(false));
server.xyz( &mock_foo );
Anywhere this fails, we immediately got the info (e.g. stacktrace).
Matchers
--------
Matchers are predicates
Gt(0)
_
User can create arbitrary matchers.
Matcher are composable
EXPECT_CALL( mock_turtle,
goTo(0, AllOf(Gt(0), Lt(10)))
);
Cardinalities
-------------
Times(2)
Times(AtLeast(3))
Actions (optional)
------------------
If they are not given, default actions will be taken.
Return(true)
DoAll( action1, ..., actionN);
ReturnRef(object);
Invoke(function)
Simulating failures
-------------------
EXPECT_CALL( mock_file, Read( _, 256))
.Times(AtLeast(3))
.WillOnce(Return(256))
.WillOnce(Return(256))
.WillRepeatedly(Return(0));
Testing logs
------------
ScopedMockLog log;
EXPECT_CALL( log, Log(INFO, "path/to/foo.cc", HasSubstr("Foo")))
.Times(AtLeast(1));
Order of calls
--------------
Strict sequence order (total order):
{
InSequence s;
EXPECT_CALL(...);
EXPECT_CALL(...);
EXPECT_CALL(...);
}
Also partial order
Also can express any DAG
gMock is extensible
IMPLEMENTATION DETAILS
======================
MOCK_METHOD2( DoThat, bool(int n, double x));
MOCK_METHOD3( Foo,
bool( char (*fp)(),
char s[5],
map<int, string> m)
);
SAMPLE
======
class Turtle {
virtual ~Turtle() {}
virtual void PenUp() = 0;
virtual void PenDown() = 0;
virtual void Forward(int distance) = 0;
virtual void Turn(int degrees) = 0;
virtual void GoTo(int x, int y) = 0;
virtual int GetX() const = 0;
virtual int GetY() const = 0;
};
#ifndef PAINTER_H
#define PAINTER_H
#include "turtle.h"
class Painter
{
public:
Painter( Turtle *trt);
bool DrawCircle(int x, int y, int r);
private:
Turtle *turtle;
};
#endif
#include "painter.h"
Painter::Painter( Turtle *trt) : turtle(trt) { }
bool Painter::DrawCircle( int x, int y, int r)
{
return true;
}
#include <gmock/gmock.h>
class MockTurtle : public Turtle
{
public:
MOCK_METHOD0( PenUp, void() );
MOCK_METHOD0( PenDown, void() );
MOCK_METHOD1( Forward, void (int distance) );
MOCK_METHOD1( Turn, void (int degrees) );
MOCK_METHOD2( GoTo, void (int x, int y) );
MOCK_CONST_METHOD( GetX, int () );
MOCK_CONST_METHOD( GetY, int () );
};
One can use the gmock_gen.py script to generate the mock class.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "painter.h"
#include "mock_turtle.h"
using ::testing::AtLeast;
TEST(PainterTest, PenDownBeforeDraw)
{
MockTurtle turtle;
EXPECT_CALL(turtle, PenDown())
.Times(AtLeast(1));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}
int main(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
$ g++ -std=c++11 -pedantic -Wall -W -I../../googletest/googletest/include/
-I../../googletest/googlemock/include/ painter.cpp test1.cpp
../../lib/libgmock.a -pthread -o test1
$ ./test1
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN ] PainterTest.PenDownBeforeDraw
test1.cpp:13: Failure
Actual function call count doesn't match EXPECT_CALL(turtle, PenDown())...
Expected: to be called at least once
Actual: never called - unsatisfied and active
[ FAILED ] PainterTest.PenDownBeforeDraw (0 ms)
[----------] 1 test from PainterTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] PainterTest.PenDownBeforeDraw
1 FAILED TEST
Fix it in painter.cpp:
#include "painter.h"
Painter::Painter( Turtle *trt) : turtle(trt) { }
bool Painter::DrawCircle( int x, int y, int r)
{
turtle->PenDown();
return true;
}
$ ./test1
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN ] PainterTest.PenDownBeforeDraw
[ OK ] PainterTest.PenDownBeforeDraw (0 ms)
[----------] 1 test from PainterTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
The generic form of the EXPECT_CALL is:
EXPECT_CALL(mock_object, method(matchers))
.With(multi_argument_matcher)
.Times(cardinality)
.InSequence(sequences)
.After(expectations)
.WillOnce(action)
.WillRepeatedly(action)
.RetiresOnSaturation();
Times
=====
If Times() is omitted the default is:
- Times(1) when neither WillOnce nor WillRepeatedly specified
- Times(n) when n WillOnes and no WillRepeatedly specified (n>=1)
- Times(AtLeast(n)) when n WillOnes and a WillRepeatedly specified (n>=0)
Times(0) means the method must not be called.
Cardinalities are:
AnyNumber()
AtLeast(n)
AtMost(n)
Between(m, n)
Exactly(n)
n
0
Matchers
========
Are used inside EXPECT_CALL() or directly:
EXPECT_THAT( value, matcher)
ASSERT_THAT( value, matcher)
Wildcard
--------
_ underscore: any value of the correct type
A<type>() An<type>() any value of "type"
Comparison
----------
Eq(value)
value arg == value
Ge(value) arg >= value
Gt(value)
Le(value)
Lt(value)
Ne(value)
IsNull() null ptr (raw or smart)
NotNull() not null ptr (raw or smart)
Ref(variable) arg is reference
TypedEq<type>(value) arg has type "type" and equal to value
These matchers (except Ref) make copy of value. If type of value is
not copyable, try byRef(), e.q.
Eq(ByRef(non_copyable_value))
DoubleEq(dvalue) NaNs are unequal.
FloatEq(fvalue)
NanSensitiveDoubleEq(dvalue) NaNs are equal.
NanSensitiveFloatEq(fvalue)
DoubleNear(dvalue, maxerr)
...
ContainsRegex(string)
EndsWith(suffix)
HasSubstr(string)
MatchesRegex(string) matches from first to last pos.
StartsWith(prefix)
StrCaseEq(string) ignoring case
StrCaseNeq(string) ignoring case
StrEq(string)
StrNe(string)
Container matchers
------------------
STL containers can be checked with Eq, since they support ==
Contains(e) Argument containes element e, e can be a further matcher
Each(e) Every element matches e
ElementsAre(e0, e1, ..., en) (max 0..10 arguments)
AlementsAreArray(...) values coming from C array, init list, STL container
IsEmpty()
SizeIs(m)
UnorderedElementsAre(e0, e1, ..., en)
WhenSorted(m)
WhenSortedBy(comparator,m)
Member matchers
---------------
Field( &class:field, m) argobj.field or argptr->field matches m
Key(e) arg.first matches e e.g. Contains(Key(Le(5)))
Pair(m1,m2) std::pair, first matches m1, second matches m2
Functor
-------
ResultOf(f,m) f function/functor, f(args) matches m
Pointer
-------
Pointee(m) arg is (raw or smart) pointer pointing something matches m
WhenDynamicCastTo<T>(m) dynamic_cast<T>(arg) matches m
Composite matchers
------------------
AllOf(m1, m2, ..., mN) mathes all of m1 ... mN
AnyOf(m1, m2, ..., mN) at least one
Not(m) does not match m
User defined matchers
---------------------
- MATCHER macros must be used outside a function or class
- must not be side effect
- PrintToString(x) converts x value to string
MATCHER(IsEven, "") { return (arg % 2) == 0; }
ACTIONS
=======
Return() void
Return(value)
ReturnArg<N>() N-th arg
ReturnNew<T>(a1, ..., ak) new T(a1,...,ak)
ReturnNull()
ReturnPointee(ptr)
ReturnRef(variable)
ReturnRefOfCopy(value) coyp lives as long as action
Assign(&variable, value)
DeleteArg<N>()
SaveArg<N>(pointer) *pointer = N-th arg
...
Throw(exception)
Invoke(f) call f with args passed to mock function
Invoke(object_pointer, &class::method)
InvokeWithoutArgs(f)
InvokeWithoutArgs(object_pointer, &class::method)
Expectation order
=================
using ::testing::Expectation;
Expectation init_x = EXPECT_CALL(foo, InitX());
Expectation init_y = EXPECT_CALL(foo, InitY());
EXPECT_CALL(foo, Bar())
.After(init_x, init_y);
using ::testing::ExpectationSet;
ExpectationSet all_inits;
for (int i = 0; i < element_count; i++)
{
all_inits += EXPECT_CALL(foo, InitElement(i));
}
EXPECT_CALL(foo, Bar())
.After(all_inits);
Sequences
=========
First Reset() than any of GetSize() or Describe():
using ::testing::Sequence;
Sequence s1, s2;
EXPECT_CALL(foo, Reset())
.InSequence(s1, s2)
.WillOnce(Return(true));
EXPECT_CALL(foo, GetSize())
.InSequence(s1)
.WillOnce(Return(1));
EXPECT_CALL(foo, Describe(A<const char*>()))
.InSequence(s2)
.WillOnce(Return("dummy"));
Strict order:
using ::testing::InSequence;
{
InSequence dummy;
EXPECT_CALL(...)...;
EXPECT_CALL(...)...;
...
EXPECT_CALL(...)...;
}
#ifndef PAINTER_H
#define PAINTER_H
#include "turtle.h"
class Painter
{
public:
Painter( Turtle *trt);
bool DrawCircle(int x, int y, int r);
bool DrawZigzag(int n);
private:
Turtle *turtle;
};
#endif
#include "painter.h"
Painter::Painter( Turtle *trt) : turtle(trt) { }
bool Painter::DrawCircle( int x, int y, int r)
{
turtle->PenDown();
return true;
}
bool Painter::DrawZigzag(int n)
{
turtle->PenDown();
for (int i = 0; i < n; ++i)
{
turtle->Turn(10);
turtle->Forward(5);
}
return true;
}
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "painter.h"
#include "mock_turtle.h"
using ::testing::AtLeast;
using ::testing::Ge;
TEST(PainterTest, PenDownBeforeDraw)
{
MockTurtle turtle;
EXPECT_CALL(turtle, PenDown())
.Times(AtLeast(1));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}
TEST(PainterTest, XwithZigzag)
{
MockTurtle turtle;
EXPECT_CALL(turtle, Forward(Ge(2)))
.Times(AtLeast(3));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawZigzag(4));
}
int main(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
$ ./test1
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from PainterTest
[ RUN ] PainterTest.PenDownBeforeDraw
[ OK ] PainterTest.PenDownBeforeDraw (0 ms)
[ RUN ] PainterTest.XwithZigzag
GMOCK WARNING:
Uninteresting mock function call - returning directly.
Function call: PenDown()
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See http:
GMOCK WARNING:
Uninteresting mock function call - returning directly.
Function call: Turn(10)
NOTE: You can safely ignore the above warning unless this call should not happen. Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call. See http:
[ OK ] PainterTest.XwithZigzag (0 ms)
[----------] 2 tests from PainterTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[ PASSED ] 2 tests.
This warning can be surpressed by using:
NiceMock<MockTurtle>
instead of
MockTurtle
#ifndef PAINTER_H
#define PAINTER_H
#include "turtle.h"
class Painter
{
public:
Painter( Turtle *trt);
bool DrawLine(int len);
bool DrawZigzag(int n);
private:
Turtle *turtle;
};
#endif
#include "painter.h"
Painter::Painter( Turtle *trt) : turtle(trt) { }
bool Painter::DrawLine( int len)
{
turtle->PenDown();
turtle->Forward(len);
turtle->PenUp();
return true;
}
bool Painter::DrawZigzag(int n)
{
turtle->PenDown();
for (int i = 0; i < n; ++i)
{
turtle->Turn(10);
turtle->Forward(5);
}
return true;
}
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "painter.h"
#include "mock_turtle.h"
using ::testing::AtLeast;
using ::testing::Ge;
using ::testing::InSequence;
using ::testing::_;
TEST(PainterTest, PenDownBeforeDraw)
{
NiceMock<MockTurtle> turtle;
EXPECT_CALL(turtle, PenDown())
.Times(AtLeast(1));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawLine(10));
}
TEST(PainterTest, XwithZigzag)
{
NiceMock<MockTurtle> turtle;
EXPECT_CALL(turtle, Forward(Ge(2)))
.Times(AtLeast(3));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawZigzag(4));
}
TEST(PainterTest, DrawLineSequence)
{
MockTurtle turtle;
{
InSequence dummy;
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(_));
EXPECT_CALL(turtle, PenUp());
}
Painter painter(&turtle);
painter.DrawLine(4);
}
int main(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
$ ./test2
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from PainterTest
[ RUN ] PainterTest.PenDownBeforeDraw
[ OK ] PainterTest.PenDownBeforeDraw (0 ms)
[ RUN ] PainterTest.XwithZigzag
[ OK ] PainterTest.XwithZigzag (1 ms)
[ RUN ] PainterTest.DrawLineSequence
[ OK ] PainterTest.DrawLineSequence (0 ms)
[----------] 3 tests from PainterTest (1 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (1 ms total)
[ PASSED ] 3 tests.
STATE MAINTENANCE
=================
Suppose we have a configurator class:
class Configurator
{
public:
virtual ~Configurator() {}
virtual void setParamX(int n) = 0;
virtual int getParamX() = 0;
};
And we have a client class using configurator:
class Client
{
public:
Client(Configurator &cfg);
virtual ~Client() {}
void setParamX(int n);
void incParamXBy(int n);
int getParamX();
private:
Configurator & _cfg;
};
void Client::incParamXBy(int n)
{
_cfg.setParamX(_cfg.getParamX() + n);
}
Suppose that the initial value of paramX is A.
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...
Since the Client behavior relies on Configurator, to test Client
the Configurator should remember the previous paramX value:
should store a state.
class MockConfigurator : public Configurator
{
public:
int paramX;
int * paramX_ptr;
MockConfigurator()
{
paramX = 0;
paramX_ptr = ¶mX;
}
MOCK_METHOD1(setParamX, void(int n));
MOCK_METHOD0(getParamX, int());
};
We put the state into MockConfigurator. (We could store it as a global
value too, but this is more maintainable).
Here is the full program:
#ifndef CONFIGURATOR_H
#define CONFIGURATOR_H
class Configurator
{
public:
virtual ~Configurator() {}
virtual void setParamX(int n) = 0;
virtual int getParamX() = 0;
};
#endif
#ifndef MOCK_CONFIGURATOR
#define MOCK_CONFIGURATOR
#include <gmock/gmock.h>
#include "configurator.h"
class MockConfigurator : public Configurator
{
public:
int paramX;
int *paramX_ptr;
MockConfigurator()
{
paramX = 0;
paramX_ptr = ¶mX;
}
MOCK_METHOD1(setParamX, void(int n));
MOCK_METHOD0(getParamX, int());
};
#endif
#ifndef CLIENT_H
#define CLIENT_H
class Client
{
public:
Client(Configurator &cfg) : _cfg(cfg) {};
virtual ~Client() {}
void setParamX(int n);
void incParamXBy(int n);
int getParamX();
private:
Configurator & _cfg;
};
#endif
#include "configurator.h"
#include "client.h"
void Client::incParamXBy(int n)
{
_cfg.setParamX(_cfg.getParamX() + n);
}
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "mock_configurator.h"
#include "client.h"
using namespace testing;
TEST(PainterTest, PenDownBeforeDraw)
{
MockConfigurator cfg;
Client client(cfg);
int inc_value = 10;
EXPECT_CALL(cfg, getParamX())
.Times(AnyNumber())
.WillRepeatedly(ReturnPointee(cfg.paramX_ptr));
EXPECT_CALL(cfg, setParamX(cfg.paramX + 3*inc_value))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));
EXPECT_CALL(cfg, setParamX(cfg.paramX + 2*inc_value))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));
EXPECT_CALL(cfg, setParamX(cfg.paramX + inc_value))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(cfg.paramX_ptr), Return()));
client.incParamXBy(inc_value);
client.incParamXBy(inc_value);
client.incParamXBy(inc_value);
}
int main(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
$ ./test1
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN ] PainterTest.PenDownBeforeDraw
[ OK ] PainterTest.PenDownBeforeDraw (0 ms)
[----------] 1 test from PainterTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[ PASSED ] 1 test.
Other options:
--------------
- We could use precalculated values (no state is required)
- We could use Invoce action