1.3.2. Visitable Mock Objects

To obtain a visitable mock object you must:

1.3.2.1. Using Macros

The following listing contains a short example and explains the basic features when using macros.


  class MyInterfaceClass 1
  {
    public:

     virtual int access(unsigned &index) = 0;
  };

  class MyVisitableMockObject : public VisitableMockObject,  2
                                public MyInterfaceClass
  {
    public:

      MyVisitableMockObject(const String &name)
        : VisitableMockObject(name, 0)
        , MOCKPP_CONSTRUCT_MEMBERS_FOR_VISTABLE1(access) 3
      {}

      MOCKPP_VISITABLE1(MyVisitableMockObject, int, access, unsigned); 4
  };

  MyVisitableMockObject mvo("mvo");

  MOCKPP_CONTROLLER_FOR(MyVisitableMockObject, access) ctr (&mvo); 5

  ctr.addThrowable(std::string("throw"), 2); 6
  ctr.addResponseValue(1, 13); 7
  ctr.addResponseValue(2, 37);
  ctr.addResponseValue(3, lt(50u)); 8

  mvo.access(1); 9
  mvo.access(13);
  mvo.access(lt(123u)); 10

  mvo.activate(); 11

  // use the object

  mvo.verify(); 12

1

Define your interface class.

2

Derive your mock object class from VisitableMockObject and the interface class.

3

According to the type of method and the number of parameters it takes add a macro that does the construction part for all the necessary internal helper variables for that method.

4

Similarly add another macro that implements the method with its internal variables and helper methods.

5

Create a controller object for the method to set up its behaviour.

6

Add some behaviour: tell the object to throw an exception when called the first and second time.

7

More specific behaviour: depending on the parameters passed it shall respond with certain return values.

8

The former controller parameters where strict and demanded exact equality. By using constraint objects it is possible to use arbitrary tolerance. In the example the parameter must be less than 50 to return 3.

9

Describe the expected execution path and the expected parameters for the method how it shall happen in your test later. This is simply done by calling the methods.

10

Demands that the parameter value is less than 123 by using an appropriate constraint object.

11

Activate the object and use it in the tests.

12

After the tests have completed verify all conditions that are still pending, for example unused return values or exceptions.

For a deeper documentation of the methods involved with a controller see the api documentation of VisitableMockObject::Controller.

1.3.2.2. Using Mock Method Templates

The second approach with mock method templates is almost the same as before. The difference is to replace the macros and the resulting controller object with the method object and forward the call to it.

  class MyVisitableMockObject : public VisitableMockObject,
                                public MyInterfaceClass
  {
    public:

      MyVisitableMockObject(const String &name)
        : VisitableMockObject(name, 0)
        , access_mocker("access", this) 1
      {}

      virtual int access(unsigned index) 2
      {
        return access_mocker.forward(index);
      }

      virtual void access(const ConstraintHolder<unsigned> &p) const 3
      {
        return access_mocker.forward(p);
      }

      VisitableMockMethod1<int, unsigned> access_mocker; 4
  };

  MyVisitableMockObject mvmo("mvmo");

  VisitableMockMethod1<int, unsigned> &ctr (mvmo.access_mocker); 5

  ctr.addThrowable(std::string("throw"), 2);
  ctr.addResponseValue(1, 13);
  ctr.addResponseValue(2, 37);
  ctr.addResponseValue(3, eq(50u)); 6

  mvmo.access(1);
  mvmo.access(13);
  mvmo.access(eq(123));

  mvmo.activate();

  // use the object

  mvmo.verify();

1

Construct the mock method helper object.

2

Provide the method as entry point and forward the call to the method which does the actual work.

3

If you want to benefit from constraints you have to provide an overloaded method which forwards the constraint holder. This method has always return type void and should be const. Of course you can call this method only in record mode.

4

Instantiate a mock method variable. Select the class according to the parameters the method takes. There is also a template without a trailing number which handles an arbitrary parameter count.

5

For convenience create a reference to the mock method helper.

6

Set up the mock objects behaviour in the same manner as described above using the macros.

1.3.2.3. Dispatching Order

The expectations are evaluated in the following order which of course also depends on the method definition itself. Parameters to the method can only be evaluated if they exist and void methods obviously can't return any objects.

  1. verify that the expected method is called
  2. find and throw an exception based on the parameters passed
  3. if available: throw the next exception from the list created with addThrowable()
  4. throw the default exception if defined
  5. verify the values of the parameters passed
  6. find and return an object based on the parameters passed
  7. if available: return the next object from the list created with addReturnValue()
  8. return the default object

There is a subtle difference in the behaviour of versions before 1.2.x which affects topics 3. and 7. in the above list. Older versions always checked if there is an exception available and threw them in the call. Newer version on the contrary use the order from the sources.

For example take the following code fragment:


  mock.addThrowable(xx);
  mock.addReturnValue(aa);
  mock.addThrowable(yy);
  mock.addReturnValue(bb);

In this case older versions would behave as follows:

  1. throw(xx)
  2. throw(yy)
  3. return(aa)
  4. return(bb)

Newer versions on the other hand will do what is written in the code:

  1. throw(xx)
  2. return(aa)
  3. throw(yy)
  4. return(bb)

If your code relies on the old behaviour you can switch back by calling unsetThrowablesInline(). This method is available for the controller object and only influences the according method. Additionally the method is also available in the container mock object itself and affects all registered sub-objects.