mirror of
https://github.com/mat007/turtle.git
synced 2026-06-22 12:13:43 +00:00
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:
parent
af5f82a29d
commit
d29397417e
10 changed files with 437 additions and 224 deletions
|
|
@ -38,3 +38,12 @@ boostbook standalone
|
|||
<xsl:param>toc.section.depth=4
|
||||
<xsl:param>nav.layout=horizontal
|
||||
;
|
||||
|
||||
project example
|
||||
: requirements
|
||||
<include>../../../../..
|
||||
;
|
||||
|
||||
compile example/motivation.cpp ;
|
||||
compile example/getting_started.cpp ;
|
||||
compile example/customisation.cpp ;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
[section Customisation]
|
||||
[import example/customisation.cpp]
|
||||
|
||||
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 :
|
||||
|
||||
namespace user_namespace
|
||||
{
|
||||
inline mock::stream& operator<<( mock::stream& s, const user_type& t )
|
||||
{
|
||||
return s << ...
|
||||
}
|
||||
}
|
||||
[mock_stream_user_type]
|
||||
|
||||
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.
|
||||
|
|
@ -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).
|
||||
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
|
||||
#include <turtle/mock.hpp>
|
||||
[mock_use_conversions]
|
||||
|
||||
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.
|
||||
|
|
@ -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 :
|
||||
|
||||
bool custom_constraint( int actual )
|
||||
{
|
||||
return actual == 42;
|
||||
}
|
||||
[custom_constraint_free_function]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[custom_constraint_free_function_test]
|
||||
|
||||
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 :
|
||||
|
||||
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]
|
||||
|
||||
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 )
|
||||
{
|
||||
mock_view v;
|
||||
calculator c( v );
|
||||
MOCK_EXPECT( v.display ).with( custom_constraint() );
|
||||
c.add( 41, 1 );
|
||||
}
|
||||
[custom_constraint_functor_test]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[near_constraint]
|
||||
|
||||
And it would be used like this :
|
||||
|
||||
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 );
|
||||
}
|
||||
[near_constraint_test]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[near_constraint_cref_test]
|
||||
|
||||
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.
|
||||
By default this value is set to 9, but if needed it can be changed before including the library :
|
||||
|
||||
#define MOCK_MAX_ARGS 20
|
||||
#include <turtle/mock.hpp>
|
||||
[max_args]
|
||||
|
||||
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 :
|
||||
|
||||
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 )
|
||||
{
|
||||
// ...
|
||||
}
|
||||
};
|
||||
[custom_policy]
|
||||
|
||||
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.
|
||||
|
||||
The policy can then be activated by defining MOCK_ERROR_POLICY prior to including the library :
|
||||
|
||||
#define MOCK_ERROR_POLICY custom_policy
|
||||
#include <turtle/mock.hpp>
|
||||
[define_custom_policy]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
|
|
|||
24
build/boost/doc/example/calculator.hpp
Normal file
24
build/boost/doc/example/calculator.hpp
Normal 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
|
||||
174
build/boost/doc/example/customisation.cpp
Normal file
174
build/boost/doc/example/customisation.cpp
Normal 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>
|
||||
//]
|
||||
88
build/boost/doc/example/getting_started.cpp
Normal file
88
build/boost/doc/example/getting_started.cpp
Normal 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 );
|
||||
}
|
||||
//]
|
||||
}
|
||||
22
build/boost/doc/example/mock_view.hpp
Normal file
22
build/boost/doc/example/mock_view.hpp
Normal 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
|
||||
68
build/boost/doc/example/motivation.cpp
Normal file
68
build/boost/doc/example/motivation.cpp
Normal 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 );
|
||||
}
|
||||
//]
|
||||
}
|
||||
20
build/boost/doc/example/view.hpp
Normal file
20
build/boost/doc/example/view.hpp
Normal 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
|
||||
|
|
@ -1,24 +1,17 @@
|
|||
[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.
|
||||
|
||||
For all the code examples the following is assumed :
|
||||
|
||||
#define BOOST_AUTO_TEST_MAIN
|
||||
#include <boost/test/auto_unit_test.hpp>
|
||||
#include <turtle/mock.hpp>
|
||||
[prerequisite]
|
||||
|
||||
[section Create, expect, trigger, verify]
|
||||
|
||||
A simple unit test with mock objects usually splits into several phases as illustrated by :
|
||||
|
||||
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
|
||||
[phases]
|
||||
|
||||
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 :
|
||||
|
||||
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
|
||||
[verify_reset]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[expectations]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[sequence]
|
||||
|
||||
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 :
|
||||
|
||||
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 );
|
||||
}
|
||||
[several_sequences]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,79 +1,33 @@
|
|||
[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 :
|
||||
|
||||
class calculator
|
||||
{
|
||||
public:
|
||||
int add( int a, int b );
|
||||
};
|
||||
[simple_calculator]
|
||||
|
||||
Obviously writing unit tests for such a class is trivial, one of them could be :
|
||||
|
||||
BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero )
|
||||
{
|
||||
calculator c;
|
||||
BOOST_CHECK_EQUAL( 0, c.add( 0, 0 ) );
|
||||
}
|
||||
[simple_zero_plus_zero_is_zero]
|
||||
|
||||
What now if the calculator class looks more like this :
|
||||
|
||||
class view
|
||||
{
|
||||
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'
|
||||
};
|
||||
[view]
|
||||
[calculator]
|
||||
|
||||
Writing unit tests becomes a bit more tedious and requires some boiler-plate code, for instance :
|
||||
|
||||
class my_view : public view
|
||||
{
|
||||
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 );
|
||||
}
|
||||
[my_view]
|
||||
[zero_plus_zero_is_zero_without_mock_object]
|
||||
|
||||
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 :
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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 );
|
||||
}
|
||||
[mock_view]
|
||||
[zero_plus_zero_is_zero_with_mock_object]
|
||||
|
||||
and all the checks are automatically handled by the library.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue