[Mockpp-commits] mockpp/mockpp/examples/tutorial tut-chain2.dox,NONE,1.1 tut-visit2.dox,NONE,1.1
Brought to you by:
ewald-arnold
From: Ewald A. <ewa...@us...> - 2005-11-18 10:24:51
|
Update of /cvsroot/mockpp/mockpp/mockpp/examples/tutorial In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27383/mockpp/examples/tutorial Added Files: tut-chain2.dox tut-visit2.dox Log Message: add draft --- NEW FILE: tut-chain2.dox --- /*************************************************************************** tut-chain2.dox - chainable mock objects, template version ------------------- begin : Fri 18 Nov 2005 copyright : (C) 2002-2005 by Ewald Arnold email : mockpp at ewald-arnold dot de $Id: tut-chain2.dox,v 1.1 2005/11/18 10:24:30 ewald-arnold Exp $ ***************************************************************************/ /*! \page tut_chain2 \ref content \section chain2_mo Solution 3b: Chainable Mock Methods After including the the \c mockpp header files some elements from the \c mockpp namespace which will be needed later are imported to omit the prefix and enhance readability. \code using mockpp::eq; using mockpp::exactly; using mockpp::returnValue; using mockpp::throwException; \endcode A more convenient way is to place a \c \#define before including the mockpp headers which imports all these shortcut functions into the global namespace: \code \#define MOCKPP_IMPORT_ABBREVIATED \#include <mockpp/chaining/ChainingMockObjectSupport.h> \endcode The next solution of the know testing problem uses another type of advanced mock objects. It is called <em>ChainableMockObject</em> 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 \c VISITABLE is replaced by \c CHAINABLE . \code class ChainMock : public Interface , public mockpp::ChainableMockObject { public: ChainMock() : mockpp::ChainableMockObject(MOCKPP_PCHAR("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) {} \endcode 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 \c Chainer instead of the former \c Controller . \code ChainMock mock; MOCKPP_CHAINER_FOR_EXT(ChainMock, open, ext) open_chainer (&mock); \endcode 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: - a number indicating how often the ecpectation is used - a constraint for each parameter of the call - a return value or an exception to throw - a unique label to reference the expectation 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 \c expects(once()) expectation only matches once and will be ignored on future invocations while an \c 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". \code open_chainer.expects(once()) .with(eq(std::string("file1.lst"))) .before("reader"); \endcode Chainable mock objects provide a method \c stubs() as well as a method \c expects(). Internally both do exactly the same. The main difference is more of a syntactical nature. Expectations expressed with \c stubs should be used when the main intent is to return values. Whereas \c expects() is prefered when the focus lies on checking parameters. Another difference is the optional parameter to \c 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. \code 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"))); \endcode <a href="chainmock_8cpp-source.html">chainmock.cpp</a> contains the complete source code. \section chain2_examples More elaborate examples It is possible to re-write the examples from the previous section. First the \c add() method is replaced. Two expected calls with parameters are prepared together with the correct return value: - \c add(1,2) ==> 3 - \c add(99,11) ==> 110 The last statement sets the default return value -1. \code 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)); \endcode The next example rewrites the \c 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: \code read_chainer.stubs(exactly(10)) .will(returnValue(0)); read_chainer.stubs(once()) .will(throwException<int>(NetworkError())); read_chainer.stubs() .will(returnValue(1)); \endcode <a href="chainmock_8cpp-source.html">chainmock.cpp</a> contains the complete source code. Next: \ref poor_mo \ref content */ --- NEW FILE: tut-visit2.dox --- /*************************************************************************** tut-visit2.dox - visitable mock objects, template version ------------------- begin : Fri 18 Nov 2005 copyright : (C) 2002-2005 by Ewald Arnold email : mockpp at ewald-arnold dot de $Id: tut-visit2.dox,v 1.1 2005/11/18 10:24:30 ewald-arnold Exp $ ***************************************************************************/ /*! \page tut_visit2 \section visit2_mo Solution 2b: Visitable Mock Methods In the previous example each sub-expectation was created and adjusted manually. To reduce the code needed to set up the desired behaviour \c mockpp provides advanced mock objects. One of them uses the <em>visitable</em> approach. The name is derived from the fact that the behaviour is set up using some king of recording machanism, or by <em>visting</em> the mock object. First the a container mock object must be implemeneted. Similar to the first solution is must inherit from \c Interface and from \c mockpp::VisitableMockObject . All the functionality is hidden in internal variables and helper methods. There is a set of variables for each method which must be initialized in the constructor. This work is done with a macro. The macro name depends on the type of method and the number of parameters it takes. You can read more about the details in the <a href="../handbook/ch01s03.html#choosing_macro" >handbook</a>. \code class VisitMock : public Interface , public mockpp::VisitableMockObject { public: VisitMock() : mockpp::VisitableMockObject("VisitMock", 0) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_VISITABLE_EXT1(open, ext) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VISITABLE0(read) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_VISITABLE_EXT1(write, ext) , MOCKPP_CONSTRUCT_MEMBERS_FOR_VOID_VISITABLE0(close) {} \endcode Then the methods are implemented. The according code for the variables and helper methods is also hidden in macros. In the simplest case you only have to pick the according macro and add the name of the class and the method. So the definition for method \c close() is easy: \code MOCKPP_VOID_VISITABLE0(VisitMock, close); \endcode If the method has a non standard type (according to the definition of \c mockpp) you need an extended set of macros. The same applies to overloaded methods. This is necessary because the internal variables and names are derived from the methods name and parameters but need special handling. This is also explained in the according section of the <a href="../handbook/ch01s03.html#choosing_macro" >handbook</a>. \code MOCKPP_VOID_VISITABLE_EXT1(VisitMock, open, const std::string &, With Macros ext, std::string); \endcode Once the class is finished and ready to use the desired behaviour must be set up. This is done by invoking the expected methods with the expected parameters. In the example the file is opened, three lines are read and the file is closed. \code VisitMock mock; mock.open("file1.lst"); mock.read(); mock.read(); mock.read(); mock.close(); \endcode When you want to prepare the return values for the method \c read() you use a helper object which passes the values via helper methods in the desired order into the mock object. \code MOCKPP_CONTROLLER_FOR(VisitMock, read) read_controller (&mock); read_controller.addReturnValue("record-1"); read_controller.addReturnValue("record-2"); read_controller.addReturnValue("record-3"); \endcode After all the behaviour is added you have to switch the mock object into test mode by invoking \c activate() . Then you run the methods under test. Similar to the first example you should call \c verify() at the end to check for pending expectations. \code mock.activate(); Consumer consumer(&mock); consumer.load(); consumer.process(); consumer.save(); mock.verify(); } catch(std::exception &ex) { std::cout << std::endl << "Error occured.\n" << ex.what() << std::endl << std::endl; } \endcode <a href="visitmock_8cpp-source.html">visitmock.cpp</a> contains the complete source code. \section visit2_enh Enhanced functionality Visitable mock objects offer some more functionality. Suppose you have a method \c add() which receives two parameters and returns the sum.You could emulate this behaviour for some expected values by using the following statements. Additionally the method shall return -1 as error indicator for all other parameters. As an alternative you could have it throw an exception by using \c setDefaultThrowable(). \code MOCKPP_CONTROLLER_FOR(VisitMock, add) add_controller (&mock); add_controller.addResponseValue(3, 1, 2); // 1 and 2 are expected add_controller.addResponseValue(110, 99, 11); // 99 and 11 are expected add_controller.setDefaultReturnValue(-1); \endcode Another rather common problem is the simulation of runtime errors. Such errors happen usually when you don't expect them but you hardly can reproduce them. To address such testing problems you might use a \c mockpp::Throwable . The following code could be used to emulate a method that returns bytes from a network connection. The first 10 calls return 0, but the next call throws a \c NetworkError . All the following calls return 1. \code class NetworkError {}; MOCKPP_CONTROLLER_FOR(VisitMock, network_read) read_controller (&mock); read_controller.addReturnValue(0, 10); read_controller.addThrowable(mockpp::make_throwable(NetworkError())); read_controller.setDefaultValue(1); \endcode When generating such behaviour you should keep in mind that there is a determined dispatching order which is explained in the <a href="../handbook/ch01s03s02.html#visit-order" >VisitableMockObject section</a> of the handbook. Since the parameters passed to \c calculate are not exactly defined we must implement some tolerance. This can be achieved with an according constraint. In this case \c mockpp::IsCloseTo which allows a delta value. \code mock.calculate(eq<unsigned>(5,5)); mock.calculate(eq<unsigned>(5,5)); mock.calculate(eq<unsigned>(5,5)); \endcode Another constraint is used to verify the data that is written back. The important part is the appended string "processed" so only this substring is verified: \code mock.write(stringContains(std::string("processed"))); mock.write(stringContains(std::string("processed"))); \endcode In a similar manner the return value can be influenced by passing the controller object according constraints instead of exact values. \code calculate_controller.addResponseValue(10, eq<unsigned>(2,2)); calculate_controller.addResponseValue(20, eq<unsigned>(4,2)); calculate_controller.addResponseValue(30, eq<unsigned>(6,2)); \endcode <a href="visitmock_8cpp-source.html">visitmock.cpp</a> contains the complete source code. Next: \ref chain_mo \ref content */ |