After including the the mockpp
header files some elements from the mockpp
namespace which will be needed later are imported to omit the prefix and enhance readability.
using mockpp::eq; using mockpp::exactly; using mockpp::returnValue; using mockpp::throwException;
A more convenient way is to place a define
before including the mockpp headers which imports all these shortcut functions into the global namespace:
#define MOCKPP_IMPORT_ABBREVIATED #include <mockpp/chaining/ChainingMockObjectSupport.h>
The next solution of the know testing problem uses another type of advanced mock objects. It is called ChainableMockObject because the expectations are created by chaining method calls to a temporary object that moves the sub-expectations into the container mock object.
The class is implemented similar to the previous example. Only the names differ slightly. Every VISITABLE
is replaced by CHAINABLE
.
class ChainMock : public Interface , public ChainableMockObject { public: ChainMock() : ChainableMockObject("ChainMock", 0) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_CHAINABLE_EXT1(open, ext) , MOCKPP_CONSTRUCT_MEMBERS_FOR_CHAINABLE0(read) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_CHAINABLE_EXT1(write, ext) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_CHAINABLE0(close) {}
The next step after implementing the mock object is similar to the previous example, too. To create expectations you have to use a helper object as well. In this case a Chainer
instead of the former Controller
.
ChainMock mock; MOCKPP_CHAINER_FOR_EXT(ChainMock, open, ext) open_chainer (&mock);
This chainer is then used to create a temporary object which helps to pass all desired sub-expectations into the mock object. Usually you will create one or more of the following types:
When a method is invoked on a mock object, the mock object searches through its expectations from first to last (in the order of your source code) to find one that matches the invocation. An expectation matches an invocation if all of its matching rules match the invocation. After the invocation, the matching expectation might stop matching further invocations. For example, an expects
(once()) expectation only matches once and will be ignored on future invocations while an expects
(atLeastOnce()) expectation will always be matched against invocations.
At the end of the test cycle all expectations have to be "consumed" for a successful result.
So he following code is quite easy to read: the method is expected to be invoked exactly once with a parameter equal to "file1.lst". And the the call must happen before another call labelled "reader".
Chainable mock objects provide a method stubs()
as well as a method expects()
. Internally both do exactly the same. The main difference is more of a syntactical nature. Expectations expressed with stubs
should be used when the main intent is to return values. Whereas expects()
is prefered when the focus lies on checking parameters. Another difference is the optional parameter to stubs()
. If this parameter is missing the expecation is optional and has unlimited lifetime. Therefor it is no error of the stub is not called at all.
To set up a method to return a value you might choose one of the patterns below. The first call passes only one argument and the second creates a sequence of three values which will be returned one after the other.
read_chainer.stubs() .will(new ReturnStub<std::string>("record-1")); read_chainer.stubs() .will(onConsecutiveCalls(new ReturnStub<std::string>("record-1"), new ReturnStub<std::string>("record-2"), new ReturnStub<std::string>("record-3")));
chainmock.cpp contains the complete source code.
It is possible to re-write the examples from the previous section.
First the add()
method is replaced. Two expected calls with parameters are prepared together with the correct return value:
add(1,2)
==> 3add(99,11)
==> 110The last statement sets the default return value -1.
add_chainer.stubs() .with(eq(1), eq(2)) .will(returnValue(3)); add_chainer.stubs() .with(eq(99), eq(11)) .will(returnValue(110)); add_chainer.stubs() .will(returnValue(-1));
The next example rewrites the network_read()
method. As before the method shall return 10 times with a 0, then an NetworkError shall be thrown and all later calls shall return 1:
read_chainer.stubs(exactly(10)) .will(returnValue(0)); read_chainer.stubs(once()) .will(throwException<int>(NetworkError())); read_chainer.stubs() .will(returnValue(1));
chainmock.cpp contains the complete source code.