To emulate the behaviour of an object in your production code you need more than just single values that meet your expectations. Usually you need a collection of the different expectation types and some code to generate a deterministic and predictible behaviour to test a limited area.
The following methods all require a separation of the code under test and the code using it. But most of the times you would split the two parts anyway to increase maintainability.
class MyInterface { public: virtual void theMethod(unsigned param) = 0; }; class MyProductionClass : public MyInterface { public: virtual void theMethod(unsigned param) { ... } }; class MyMockObject : public MyInterface { mockpp::ExpectationList<unsigned> methParams; public: MyMockObject(mockpp::String name, Verifiable *v) : MockObject(name, v) , methParams("method parameters", this) { methParams.addExpected(1); methParams.addExpected(2); } virtual void theMethod(unsigned param) { methParams.addActual(param); ... } }; MyMockObject mo (MOCKPP_PCHAR("mockObject"), 0); myMethodUnderTest(&mo); mo.verify(); MyProductionClass pro; myMethodUnderTest(&pro);
Since most of tasks concerning mock objects occur frequently and in a similar manner there are some classes which support automated behaviour with different approaches. Depending on your personal taste or special problems you may choose the one or the other.
More detailed explanations follow in later chapters.
The basic principle for the use of these classes is the same for both methods:
But let me warn you: | |
---|---|
If you take the wrong macro or maybe just have a small typo you will get loads of error messages all pointing to the same line. You might have no idea where to start and search the problem. I strongly recommend to take little steps when setting up such objects: add one single method and compile. Continue only if it compiled flawlessly. This warning shouldn't scare you away. It isn't really that complicated and once you have understood the working method of the macros you may have understood half of mockpp. To get started I recommend to use the "recipies" below. |
If you want to have an overview what is created behind the scenes, look at chainable-template.h and visitable-template.h which contain example classes with some methods. They were preprocessed, automatically indented and are quite readable. This knowledge might become useful when you need to find out more about the referenced objects and their internal helper variables when runtime errors occur.
There are sets of macros for const and non-const methods, the visitable and the chainable way and each combination of them with up to five parameters. I assume you will not want to have code with more than 5 parameters. But if you really think you need more, take the macros with 5 parameters and try to extend it. It is really straightforward and please don't forget to extend the test files. You may then of course send me the result for inclusion and further maintainance.
Due to the nature of macros there are some limitations concerning the automatically generated methods and the according internal helper variables.
The total number of macros is quite huge but there is a scheme behind it:
The macro for the initializer list in the constructor is built like this:
To determine the macro for the method write the following:
The macro for the helper object to set up the behaviour needs these steps:
Here are some examples:
To see examples for all the macros have a look at the unit test files in the tests directory. Especially interesting are the files ChainableMockObject_* and VisitableMocObject_*.
If the available mock objects and methods don't give you fine enough control over your mock object there is only one solution: code and implement the desired behaviour manually. Look at chainable-template.h and visitable-template.h to get an idea of the standard implementation.
But I assume this will not happen too often because mock objects should only cover a very limited aspect, only what is currently really needed for a special test scope.
After the last invocation to a mock object has occured you should always invoke verify() on the mock object to verify all pending expectations. Maybe there are some unused return objects or outstanding calls. If you use cppunit as test framework you should the use VerifyingTestCase from mockpp and register your mock objects on creation with it. In contrast to the standard TestCase from the cppunit package it invokes verify() on all mock objects after a test case passed. This way it is impossible to forget it.