Turned documentation code into example code

git-svn-id: https://svn.code.sf.net/p/turtle/code/trunk@578 860be788-9bd5-4423-9f1e-828f051e677b
This commit is contained in:
mat007 2012-11-28 23:01:45 +00:00
parent af5f82a29d
commit d29397417e
10 changed files with 437 additions and 224 deletions

View file

@ -38,3 +38,12 @@ boostbook standalone
<xsl:param>toc.section.depth=4 <xsl:param>toc.section.depth=4
<xsl:param>nav.layout=horizontal <xsl:param>nav.layout=horizontal
; ;
project example
: requirements
<include>../../../../..
;
compile example/motivation.cpp ;
compile example/getting_started.cpp ;
compile example/customisation.cpp ;

View file

@ -1,4 +1,5 @@
[section Customisation] [section Customisation]
[import example/customisation.cpp]
This section explains how to customise different aspects of the library. This section explains how to customise different aspects of the library.
@ -13,13 +14,7 @@ By default the library attempts to serialize to an std::ostream and if this is n
If for some reason the serialization to an std::ostream shouldn't be used, it can be overridden by a serialization operator to a mock::stream, for instance to log user_type declared in user_namespace : If for some reason the serialization to an std::ostream shouldn't be used, it can be overridden by a serialization operator to a mock::stream, for instance to log user_type declared in user_namespace :
namespace user_namespace [mock_stream_user_type]
{
inline mock::stream& operator<<( mock::stream& s, const user_type& t )
{
return s << ...
}
}
The operator is found using [@http://en.wikipedia.org/wiki/Argument-dependent_name_lookup argument-dependent name lookup] which means it needs to be in the namespace of either one of its arguments. The operator is found using [@http://en.wikipedia.org/wiki/Argument-dependent_name_lookup argument-dependent name lookup] which means it needs to be in the namespace of either one of its arguments.
The easiest is to define it in the same namespace as the type being serialized. If this is not possible (for instance when serializing a type in namespace std because the C++ standard explicitly forbids adding definitions into the std namespace) a serialization operator to mock::stream can be in the mock namespace instead. The easiest is to define it in the same namespace as the type being serialized. If this is not possible (for instance when serializing a type in namespace std because the C++ standard explicitly forbids adding definitions into the std namespace) a serialization operator to mock::stream can be in the mock namespace instead.
@ -27,8 +22,7 @@ The easiest is to define it in the same namespace as the type being serialized.
The serialization operators detection doesn't attempt to do conversions when looking for a match (because this can sometimes yield an ambiguous resolution error). The serialization operators detection doesn't attempt to do conversions when looking for a match (because this can sometimes yield an ambiguous resolution error).
As conversions can prove convenient, for instance when dealing with a base class which is derived to a lot of sub-classes, they can be activated by defining MOCK_USE_CONVERSIONS prior to including the library : As conversions can prove convenient, for instance when dealing with a base class which is derived to a lot of sub-classes, they can be activated by defining MOCK_USE_CONVERSIONS prior to including the library :
#define MOCK_USE_CONVERSIONS [mock_use_conversions]
#include <turtle/mock.hpp>
Be aware though that in this case the compiler can produce a compilation error when attempting to detect whether serialization operators exist or not. Be aware though that in this case the compiler can produce a compilation error when attempting to detect whether serialization operators exist or not.
It is always possible however to define a serialization operator to a mock::stream in order to bypass the detection. It is always possible however to define a serialization operator to a mock::stream in order to bypass the detection.
@ -56,22 +50,13 @@ The library comes with a set of pre-defined [link turtle.reference.expectation.c
Creating a constraint can be as simple as writing a function, for instance : Creating a constraint can be as simple as writing a function, for instance :
bool custom_constraint( int actual ) [custom_constraint_free_function]
{
return actual == 42;
}
Any functor will actually do as long as its signature matches the requirement : take a type convertible from the actual type and return a boolean. Any functor will actually do as long as its signature matches the requirement : take a type convertible from the actual type and return a boolean.
Using the custom constraint is also pretty trivial, for instance : Using the custom constraint is also pretty trivial, for instance :
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) [custom_constraint_free_function_test]
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( &custom_constraint );
c.add( 41, 1 );
}
Simple enough, however this constraint isn't serializable and thus yields a rather uninformative '?' in the logs. Simple enough, however this constraint isn't serializable and thus yields a rather uninformative '?' in the logs.
@ -79,28 +64,11 @@ Just like a parameter, a constraint can be displayed in a readable form using it
Thus for a widely used constraint (for instance one shipped with the code of a library) it is likely better to define it like this : Thus for a widely used constraint (for instance one shipped with the code of a library) it is likely better to define it like this :
struct custom_constraint [custom_constraint_functor]
{
friend bool operator==( int actual, const custom_constraint& )
{
return actual == 42;
}
friend std::ostream& operator<<( std::ostream& s, const custom_constraint& )
{
return s << "_ == 42";
}
};
And of course the constraint is to be used in a slightly different way : And of course the constraint is to be used in a slightly different way :
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) [custom_constraint_functor_test]
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( custom_constraint() );
c.add( 41, 1 );
}
Actually real world use cases sometimes need several other features as well : Actually real world use cases sometimes need several other features as well :
@ -110,45 +78,11 @@ Actually real world use cases sometimes need several other features as well :
Therefore a more realistic and complete example would be : Therefore a more realistic and complete example would be :
template< typename Expected > [near_constraint]
struct near_constraint
{
near_constraint( Expected expected, Expected threshold )
: expected_( expected )
, threshold_( threshold )
{}
template< typename Actual >
bool operator()( Actual actual ) const
{
return std::abs( actual - boost::unwrap_ref( expected_ ) )
< boost::unwrap_ref( threshold_ );
}
friend std::ostream& operator<<( std::ostream& s, const near_constraint& c )
{
return s << "near( " << mock::format( c.expected_ )
<< ", " << mock::format( c.threshold_ ) << " )";
}
Expected expected_, threshold_;
};
template< typename Expected >
mock::constraint< near_constraint< Expected > > near( Expected expected, Expected threshold )
{
return near_constraint< Expected >( expected, threshold );
}
And it would be used like this : And it would be used like this :
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) [near_constraint_test]
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( near( 42, 1 ) );
c.add( 41, 1 );
}
The purpose of the 'near' template function is to : The purpose of the 'near' template function is to :
@ -157,16 +91,7 @@ The purpose of the 'near' template function is to :
The use of boost::unwrap_ref provides support for passing arguments as references with boost::ref and boost::cref and delaying their initialization, for instance : The use of boost::unwrap_ref provides support for passing arguments as references with boost::ref and boost::cref and delaying their initialization, for instance :
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) [near_constraint_cref_test]
{
mock_view v;
calculator c( v );
int expected, threshold;
MOCK_EXPECT( v.display ).with( near( boost::cref( expected ), boost::cref( threshold ) ) );
expected = 42;
threshold = 1;
c.add( 41, 1 );
}
See [link turtle.reference.expectation.constraints constraints] for an explanation of how the library detects whether an argument is a functor or a value. See [link turtle.reference.expectation.constraints constraints] for an explanation of how the library detects whether an argument is a functor or a value.
@ -179,8 +104,7 @@ For more information about the serialization operator and the use of mock::forma
The maximum number of arguments a mocked method can have is defined by MOCK_MAX_ARGS. The maximum number of arguments a mocked method can have is defined by MOCK_MAX_ARGS.
By default this value is set to 9, but if needed it can be changed before including the library : By default this value is set to 9, but if needed it can be changed before including the library :
#define MOCK_MAX_ARGS 20 [max_args]
#include <turtle/mock.hpp>
This means methods with up to 20 arguments will then be accepted. This means methods with up to 20 arguments will then be accepted.
@ -205,37 +129,14 @@ By default the library expects to be used in conjunction with Boost.Test e.g. :
However integrating with any given unit test framework can be done by defining a custom error policy implementing the following concept : However integrating with any given unit test framework can be done by defining a custom error policy implementing the following concept :
template< typename Result > [custom_policy]
struct custom_policy
{
static Result abort()
{
// ...
}
template< typename Context >
static void fail( const char* message, const Context&, const char* file = "unknown location", int line = 0 )
{
// ...
}
template< typename Context >
static void call( const Context& context, const char* file, int line )
{
// ...
}
template< typename Context >
static void pass( const char* file, int line )
{
// ...
}
};
The context, which stands for "something serializable to an std::ostream", is actually built only if an attempt to serialize it is made, thus enabling lazy serialization of all elements (e.g. constraints and parameters). The context, which stands for "something serializable to an std::ostream", is actually built only if an attempt to serialize it is made, thus enabling lazy serialization of all elements (e.g. constraints and parameters).
File and line show were the expectation has been configured. File and line show were the expectation has been configured.
The policy can then be activated by defining MOCK_ERROR_POLICY prior to including the library : The policy can then be activated by defining MOCK_ERROR_POLICY prior to including the library :
#define MOCK_ERROR_POLICY custom_policy [define_custom_policy]
#include <turtle/mock.hpp>
[endsect] [endsect]

View file

@ -0,0 +1,24 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef CALCULATOR
#define CALCULATOR
class view;
//[ calculator
class calculator
{
public:
calculator( view& v );
void add( int a, int b ); // the result will be sent to the view 'v'
};
//]
#endif // CALCULATOR

View file

@ -0,0 +1,174 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//[ mock_use_conversions
#define MOCK_USE_CONVERSIONS
#include <turtle/mock.hpp>
//]
#include "calculator.hpp"
#include "mock_view.hpp"
//[ mock_stream_user_type
namespace user_namespace
{
struct user_type
{};
inline mock::stream& operator<<( mock::stream& s, const user_type& )
{
return s << "user_type";
}
}
//]
namespace custom_constraint_free_function_test
{
//[ custom_constraint_free_function
bool custom_constraint( int actual )
{
return actual == 42;
}
//]
//[ custom_constraint_free_function_test
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( &custom_constraint );
c.add( 41, 1 );
}
//]
}
namespace custom_constraint_functor_test
{
//[ custom_constraint_functor
struct custom_constraint
{
friend bool operator==( int actual, const custom_constraint& )
{
return actual == 42;
}
friend std::ostream& operator<<( std::ostream& s, const custom_constraint& )
{
return s << "_ == 42";
}
};
//]
//[ custom_constraint_functor_test
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( custom_constraint() );
c.add( 41, 1 );
}
//]
}
//[ near_constraint
template< typename Expected >
struct near_constraint
{
near_constraint( Expected expected, Expected threshold )
: expected_( expected )
, threshold_( threshold )
{}
template< typename Actual >
bool operator()( Actual actual ) const
{
return std::abs( actual - boost::unwrap_ref( expected_ ) )
< boost::unwrap_ref( threshold_ );
}
friend std::ostream& operator<<( std::ostream& s, const near_constraint& c )
{
return s << "near( " << mock::format( c.expected_ )
<< ", " << mock::format( c.threshold_ ) << " )";
}
Expected expected_, threshold_;
};
template< typename Expected >
mock::constraint< near_constraint< Expected > > near( Expected expected, Expected threshold )
{
return near_constraint< Expected >( expected, threshold );
}
//]
namespace near_constraint_test
{
//[ near_constraint_test
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).with( near( 42, 1 ) );
c.add( 41, 1 );
}
//]
}
namespace near_constraint_cref_test
{
//[ near_constraint_cref_test
BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one )
{
mock_view v;
calculator c( v );
int expected, threshold;
MOCK_EXPECT( v.display ).with( near( boost::cref( expected ), boost::cref( threshold ) ) );
expected = 42;
threshold = 1;
c.add( 41, 1 );
}
//]
}
#undef MOCK_MAX_ARGS
//[ max_args
#define MOCK_MAX_ARGS 20
#include <turtle/mock.hpp>
//]
//[ custom_policy
template< typename Result >
struct custom_policy
{
static Result abort()
{
// ...
}
template< typename Context >
static void fail( const char* message, const Context&, const char* file = "unknown location", int line = 0 )
{
// ...
}
template< typename Context >
static void call( const Context& context, const char* file, int line )
{
// ...
}
template< typename Context >
static void pass( const char* file, int line )
{
// ...
}
};
//]
#undef MOCK_ERROR_POLICY
//[ define_custom_policy
#define MOCK_ERROR_POLICY custom_policy
#include <turtle/mock.hpp>
//]

View file

@ -0,0 +1,88 @@
//[ prerequisite
#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <turtle/mock.hpp>
//]
#include "calculator.hpp"
#include "mock_view.hpp"
namespace phases
{
//[ phases
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v; // create mock objects
calculator c( v ); // create object under test
MOCK_EXPECT( v.display ).once().with( 0 ); // configure mock objects
c.add( 0, 0 ); // exercise object under test
} // verify mock objects
//]
}
namespace verify_reset
{
//[ verify_reset
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 );
c.add( 0, 0 );
MOCK_VERIFY( v.display ); // verify all expectations are fulfilled for the 'display' method
mock::verify( v ); // verify all expectations are fulfilled for all methods of 'v'
mock::verify(); // verify all expectations are fulfilled for all existing mock objects
MOCK_RESET( v.display ); // reset all expectations for the 'display' method
mock::reset( v ); // reset all expectations for all methods of 'v'
mock::reset(); // reset all expectations for all existing mock objects
} // automatically verify all expectations are fulfilled for all mock objects going out of scope
//]
}
namespace expectations
{
//[ expectations
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 ); // this call must occur once (and only once)
MOCK_EXPECT( v.display ).with( 1 ); // this call can occur any number of times (including never)
c.add( 0, 0 );
}
//]
}
namespace sequence
{
//[ sequence
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
mock::sequence s;
MOCK_EXPECT( v.display ).once().with( 0 ).in( s ); // add this expectation to the sequence
MOCK_EXPECT( v.display ).with( 1 ).in( s ); // add this expectation to the sequence after the previous one
c.add( 0, 0 );
c.add( 1, 0 );
}
//]
}
namespace several_sequences
{
//[ several_sequences
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
mock::sequence s1, s2;
MOCK_EXPECT( v.display ).once().with( 0 ).in( s1 );
MOCK_EXPECT( v.display ).once().with( 1 ).in( s2 );
MOCK_EXPECT( v.display ).with( 2 ).in( s1 ).in( s2 ); // add this expectation to both sequences after the previous ones
c.add( 0, 0 );
c.add( 1, 0 );
c.add( 1, 1 );
c.add( 2, 0 );
}
//]
}

View file

@ -0,0 +1,22 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef MOCK_VIEW
#define MOCK_VIEW
#include <turtle/mock.hpp>
#include "view.hpp"
//[ mock_view
MOCK_BASE_CLASS( mock_view, view ) // declare a 'mock_view' class implementing 'view'
{
MOCK_METHOD( display, 1 ) // implement the 'display' method from 'view' (taking 1 argument)
};
//]
#endif // MOCK_VIEW

View file

@ -0,0 +1,68 @@
#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <boost/mock/mock.hpp>
#include "calculator.hpp"
#include "mock_view.hpp"
namespace simple
{
//[ simple_calculator
class calculator
{
public:
int add( int a, int b );
};
//]
//[ simple_zero_plus_zero_is_zero
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
calculator c;
BOOST_CHECK_EQUAL( 0, c.add( 0, 0 ) );
}
//]
}
namespace without_mock_object
{
//[ my_view
class my_view : public view
{
public:
my_view()
: called( false )
{}
virtual void display( int result )
{
called = true;
value = result;
}
bool called;
int value;
};
//]
//[ zero_plus_zero_is_zero_without_mock_object
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
my_view v;
calculator c( v );
c.add( 0, 0 );
BOOST_REQUIRE( v.called );
BOOST_CHECK_EQUAL( 0, v.value );
}
//]
}
namespace with_mock_object
{
//[ zero_plus_zero_is_zero_with_mock_object
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 ); // expect the 'display' method to be called once (and only once) with a parameter value equal to 0
c.add( 0, 0 );
}
//]
}

View file

@ -0,0 +1,20 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef VIEW
#define VIEW
//[ view
class view
{
public:
virtual void display( int result ) = 0;
};
//]
#endif // VIEW

View file

@ -1,24 +1,17 @@
[section Getting Started] [section Getting Started]
[import example/getting_started.cpp]
This section introduces most of the library features in a series of use cases built on the example from the [link turtle.motivation motivation] section. This section introduces most of the library features in a series of use cases built on the example from the [link turtle.motivation motivation] section.
For all the code examples the following is assumed : For all the code examples the following is assumed :
#define BOOST_AUTO_TEST_MAIN [prerequisite]
#include <boost/test/auto_unit_test.hpp>
#include <turtle/mock.hpp>
[section Create, expect, trigger, verify] [section Create, expect, trigger, verify]
A simple unit test with mock objects usually splits into several phases as illustrated by : A simple unit test with mock objects usually splits into several phases as illustrated by :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [phases]
{
mock_view v; // create mock objects
calculator c( v ); // create object under test
MOCK_EXPECT( v.display ).once().with( 0 ); // configure mock objects
c.add( 0, 0 ); // exercise object under test
} // verify mock objects
Triggering the object under test in turn calls methods on the mock objects, and any unexpected call raises an error. Triggering the object under test in turn calls methods on the mock objects, and any unexpected call raises an error.
@ -28,19 +21,7 @@ More sophisticated tests sometimes require more complex use cases and in particu
Here is an example highlighting the different possibilities : Here is an example highlighting the different possibilities :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [verify_reset]
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 );
c.add( 0, 0 );
MOCK_VERIFY( v.display ); // verify all expectations are fulfilled for the 'display' method
mock::verify( v ); // verify all expectations are fulfilled for all methods of 'v'
mock::verify(); // verify all expectations are fulfilled for all existing mock objects
MOCK_RESET( v.display ); // reset all expectations for the 'display' method
mock::reset( v ); // reset all expectations for all methods of 'v'
mock::reset(); // reset all expectations for all existing mock objects
} // automatically verify all expectations are fulfilled for all mock objects going out of scope
Note that all verifications upon destruction will be disabled if the mock objects are destroyed in the context of an exception being raised. Note that all verifications upon destruction will be disabled if the mock objects are destroyed in the context of an exception being raised.
@ -50,14 +31,7 @@ Note that all verifications upon destruction will be disabled if the mock object
A method can be configured with several expectations, for instance : A method can be configured with several expectations, for instance :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [expectations]
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 ); // this call must occur once (and only once)
MOCK_EXPECT( v.display ).with( 1 ); // this call can occur any number of times (including never)
c.add( 0, 0 );
}
Each method call is then handled by processing the expectations in the order they have been defined : Each method call is then handled by processing the expectations in the order they have been defined :
@ -68,34 +42,13 @@ An error is raised if none can be found.
By default the relative order of the calls does not matter. It can however be enforced : By default the relative order of the calls does not matter. It can however be enforced :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [sequence]
{
mock_view v;
calculator c( v );
mock::sequence s;
MOCK_EXPECT( v.display ).once().with( 0 ).in( s ); // add this expectation to the sequence
MOCK_EXPECT( v.display ).with( 1 ).in( s ); // add this expectation to the sequence after the previous one
c.add( 0, 0 );
c.add( 1, 0 );
}
Therefore an error will be issued if the second expectation is matched before the first one has been exhausted. Therefore an error will be issued if the second expectation is matched before the first one has been exhausted.
An expectation can be part of several sequences : An expectation can be part of several sequences :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [several_sequences]
{
mock_view v;
calculator c( v );
mock::sequence s1, s2;
MOCK_EXPECT( v.display ).once().with( 0 ).in( s1 );
MOCK_EXPECT( v.display ).once().with( 1 ).in( s2 );
MOCK_EXPECT( v.display ).with( 2 ).in( s1 ).in( s2 ); // add this expectation to both sequences after the previous ones
c.add( 0, 0 );
c.add( 1, 0 );
c.add( 1, 1 );
c.add( 2, 0 );
}
[endsect] [endsect]

View file

@ -1,79 +1,33 @@
[section Motivation] [section Motivation]
[import example/motivation.cpp]
[import example/calculator.hpp]
[import example/mock_view.hpp]
[import example/view.hpp]
Consider a (very) simple calculator class : Consider a (very) simple calculator class :
class calculator [simple_calculator]
{
public:
int add( int a, int b );
};
Obviously writing unit tests for such a class is trivial, one of them could be : Obviously writing unit tests for such a class is trivial, one of them could be :
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) [simple_zero_plus_zero_is_zero]
{
calculator c;
BOOST_CHECK_EQUAL( 0, c.add( 0, 0 ) );
}
What now if the calculator class looks more like this : What now if the calculator class looks more like this :
class view [view]
{ [calculator]
public:
virtual void display( int result ) = 0;
};
class calculator
{
public:
calculator( view& v );
void add( int a, int b ); // the result will be sent to the view 'v'
};
Writing unit tests becomes a bit more tedious and requires some boiler-plate code, for instance : Writing unit tests becomes a bit more tedious and requires some boiler-plate code, for instance :
class my_view : public view [my_view]
{ [zero_plus_zero_is_zero_without_mock_object]
public:
my_view()
: called( false )
{}
virtual void display( int result )
{
called = true;
value = result;
}
bool called;
int value;
};
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
my_view v;
calculator c( v );
c.add( 0, 0 );
BOOST_REQUIRE( v.called );
BOOST_CHECK_EQUAL( 0, v.value );
}
Mock objects main purpose is to alleviate the user from the burden of writing all this boiler-plate code. Mock objects main purpose is to alleviate the user from the burden of writing all this boiler-plate code.
Here is how the last test can be rewritten using a mock object : Here is how the last test can be rewritten using a mock object :
MOCK_BASE_CLASS( mock_view, view ) // declare a 'mock_view' class implementing 'view' [mock_view]
{ [zero_plus_zero_is_zero_with_mock_object]
MOCK_METHOD( display, 1 ) // implement the 'display' method from 'view' (taking 1 argument)
};
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
{
mock_view v;
calculator c( v );
MOCK_EXPECT( v.display ).once().with( 0 ); // expect the 'display' method to be called once (and only once) with a parameter value equal to 0
c.add( 0, 0 );
}
and all the checks are automatically handled by the library. and all the checks are automatically handled by the library.