[Mockpp-commits] mockpp/mockpp/examples/tutorial tut-chain2.dox,1.2,1.3 tut-visit2.dox,1.2,1.3 tutor
Brought to you by:
ewald-arnold
From: Ewald A. <ewa...@us...> - 2005-11-18 18:07:51
|
Update of /cvsroot/mockpp/mockpp/mockpp/examples/tutorial In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7520/mockpp/examples/tutorial Modified Files: tut-chain2.dox tut-visit2.dox tutorial.doxygen.in Log Message: update Index: tut-visit2.dox =================================================================== RCS file: /cvsroot/mockpp/mockpp/mockpp/examples/tutorial/tut-visit2.dox,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- tut-visit2.dox 18 Nov 2005 10:58:55 -0000 1.2 +++ tut-visit2.dox 18 Nov 2005 18:07:38 -0000 1.3 @@ -1,185 +1,124 @@ /*************************************************************************** - 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 - 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$ + * ***************************************************************************/ /*! \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 +The former approach to set up a visitable mock objects with macros +needs few lines of code and works fine if you do everything right. -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: +But a big disadvantage is the fact that it is really hard to find +compiler errors as all messages refer to the same line. For the same +reason it is almost impossible to use a debugger and step through the +code and follow the execution path. -\code - MOCKPP_VOID_VISITABLE0(VisitMock, close); -\endcode +A better solution is the use of mock methods. They act similar to the code +behind the macros but are based on templates. There are only little +changes. Most of the work is it to provide some forwarder +methods. -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>. +Include the additonal top level header: \code - MOCKPP_VOID_VISITABLE_EXT1(VisitMock, open, const std::string &, - With Macros ext, std::string); +#include <mockpp/CountedVisitableMethod.h> \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. +Instead of providing the members wrapped in macros add the mock method +objects. \code - VisitMock mock; +class VisitMock : public Interface + , public VisitableMockObject +{ + public: - mock.open("file1.lst"); - mock.read(); - mock.read(); - mock.read(); - mock.close(); + VisitMock() + : VisitableMockObject(MOCKPP_PCHAR("VisitMock"), 0) + , open_mocker(MOCKPP_PCHAR("open"), this) + , read_mocker(MOCKPP_PCHAR("read"), this) + , write_mocker(MOCKPP_PCHAR("write"), this) + , close_mocker(MOCKPP_PCHAR("close"), this) + , calculate_mocker(MOCKPP_PCHAR("calculate"), this) + {} \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. +Implementing the methods within the mock object is easy as they only +forward to their mock method object. \code - MOCKPP_CONTROLLER_FOR(VisitMock, read) read_controller (&mock); + void open(const std::string &filename) + { + open_mocker.forward(filename); + } - read_controller.addReturnValue("record-1"); - read_controller.addReturnValue("record-2"); - read_controller.addReturnValue("record-3"); + std::string read() + { + return read_mocker.forward(); + } \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. +If you intend to use constraints to make your tests less strict you need +to add an overloaded method which forwards the constraint object. Note that +this method may only be called in record mode and never returns a value. \code - mock.activate(); - - Consumer consumer(&mock); - consumer.load(); - consumer.process(); - consumer.save(); + unsigned calculate(unsigned input) + { + return calculate_mocker.forward(input); + } - mock.verify(); - } - catch(std::exception &ex) - { - std::cout << std::endl - << "Error occured.\n" << ex.what() << std::endl - << std::endl; - } + void calculate(const ConstraintHolder<unsigned> &ch) + { + calculate_mocker.forward(ch); + } \endcode -<a href="visitmock2_8cpp-source.html">visitmock2.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(). +To finally complete the class definition you have to declare the mock +methods. \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); + VisitableMockMethod<void, std::string> open_mocker; + VisitableMockMethod<std::string> read_mocker; + VisitableMockMethod<void, std::string> write_mocker; + VisitableMockMethod<void> close_mocker; + VisitableMockMethod<unsigned, unsigned> calculate_mocker; \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. +Now as everything is prepared you set up the behaviour. This happens +almost exactly as before when using macros. Instead of using a controller +object you use the mock methods directly. For convenience, in case you +have rather long names as in the example, you can use a reference as well. \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. + VisitMock mock; -\code - mock.calculate(eq<unsigned>(5,5)); - mock.calculate(eq<unsigned>(5,5)); - mock.calculate(eq<unsigned>(5,5)); + VisitableMockMethod<std::string> &reader (mock.read_mocker); + VisitableMockMethod<unsigned, unsigned> &calcer (mock.calculate_mocker); \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: +The actual setup is done with exactly the same methods as before with +the macros. \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. + mock.open("file1.lst"); + mock.read(); + mock.read(); + mock.read(); + mock.close(); -\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)); + reader.addReturnValue("record-1"); + reader.addReturnValue("record-2"); + reader.addReturnValue("record-3"); \endcode <a href="visitmock2_8cpp-source.html">visitmock2.cpp</a> contains the complete source code. @@ -189,4 +128,3 @@ \ref content */ - Index: tut-chain2.dox =================================================================== RCS file: /cvsroot/mockpp/mockpp/mockpp/examples/tutorial/tut-chain2.dox,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- tut-chain2.dox 18 Nov 2005 10:58:55 -0000 1.2 +++ tut-chain2.dox 18 Nov 2005 18:07:38 -0000 1.3 @@ -16,155 +16,60 @@ \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. +Similar to the steps taken from the macro version of the visitable +mock object to the one with mock methods you have to replace the macro code +with the according method template members. -The class is implemented similar to the previous example. -Only the names differ slightly. Every \c VISITABLE is replaced by -\c CHAINABLE . +And in the same manner you must add the forwarder methods. Due to the +different working method there is no need to provide an overloaded +method for constraints. \code class ChainMock : public Interface - , public mockpp::ChainableMockObject + , public 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) + : ChainableMockObject(MOCKPP_PCHAR("ChainMock"), 0) + , open_mocker(MOCKPP_PCHAR("open"), this) + , read_mocker(MOCKPP_PCHAR("read"), this) + , write_mocker(MOCKPP_PCHAR("write"), this) + , close_mocker(MOCKPP_PCHAR("close"), this) + , calculate_mocker(MOCKPP_PCHAR("calculate"), this) {} -\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")); + void open(const std::string &filename) + { + open_mocker.forward(filename); + } - read_chainer.stubs() - .will(onConsecutiveCalls(new ReturnStub<std::string>("record-1"), - new ReturnStub<std::string>("record-2"), - new ReturnStub<std::string>("record-3"))); + ChainableMockMethod<void, std::string> open_mocker; + ChainableMockMethod<std::string> read_mocker; + ChainableMockMethod<void, std::string> write_mocker; + ChainableMockMethod<void> close_mocker; + ChainableMockMethod<unsigned, unsigned> calculate_mocker; +}; \endcode -<a href="chainmock2_8cpp-source.html">chainmock2.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. +Now create the mock object and a reference to the mock method you intend to use. \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)); + ChainMock mock; + ChainableMockMethod<std::string> &reader(mock.read_mocker); \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: +The only action left is to set up the behaviour. This is done with exactly the +same method invocations as in the example with the macros. \code - read_chainer.stubs(exactly(10)) - .will(returnValue(0)); - - read_chainer.stubs(once()) - .will(throwException<int>(NetworkError())); + reader.stubs() + .will(new ReturnStub<std::string>("record-1")); - read_chainer.stubs() - .will(returnValue(1)); + reader.stubs() + .will(onConsecutiveCalls(new ReturnStub<std::string>("record-1"), + new ReturnStub<std::string>("record-2"), + new ReturnStub<std::string>("record-3"))); \endcode <a href="chainmock2_8cpp-source.html">chainmock2.cpp</a> contains the Index: tutorial.doxygen.in =================================================================== RCS file: /cvsroot/mockpp/mockpp/mockpp/examples/tutorial/tutorial.doxygen.in,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- tutorial.doxygen.in 24 Apr 2005 11:35:17 -0000 1.6 +++ tutorial.doxygen.in 18 Nov 2005 18:07:38 -0000 1.7 @@ -16,7 +16,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO -STRIP_FROM_PATH = +STRIP_FROM_PATH = SRCDIR/ INTERNAL_DOCS = NO STRIP_CODE_COMMENTS = YES CASE_SENSE_NAMES = NO @@ -48,7 +48,7 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = NO WARN_FORMAT = -WARN_LOGFILE = +WARN_LOGFILE = warnfile.txt #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- |