Table of contents

Solution 3a: Chainable Mock Objects With Macros

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".

    open_chainer.expects(once())
                .with(eq(std::string("file1.lst")))
                .before("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.

More elaborate examples

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:

The 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.

Next: Solution 3b: Chainable Mock Methods

Table of contents

 All Classes Namespaces Files Functions Variables Typedefs Friends Defines

Generated on Tue Jan 5 18:03:33 2010 for mockpp-tutorial by  doxygen 1.6.1