Download | News | Support | Project |
This section highlights not-so-obvious features of the library gathered from real use cases.
Problem :
#define BOOST_AUTO_TEST_MAIN #include <boost/test/auto_unit_test.hpp> #include <turtle/mock.hpp> #include <ostream> namespace { struct my_class { my_class( int i ) : i_( i ) {} int i_; }; std::ostream& operator<<( std::ostream& os, const my_class* c ) { return os << "my_class " << c->i_; // the 'c' pointer must be valid when logging } MOCK_FUNCTION( f, 1, void( my_class* ) ) // being static 'f' outlive the test case } BOOST_AUTO_TEST_CASE( static_objects_problem ) { my_class c( 42 ); MOCK_EXPECT( f ).once().with( &c ); // the set expectation will also outlive the test case and leak into other test cases using 'f' } // the 'c' instance goes out of scope and the '&c' pointer becomes dangling
Partial solution :
struct fixture { ~fixture() { mock::reset(); // the use of a fixture ensures the reset will prevent the expectations from leaking into other test cases } }; BOOST_FIXTURE_TEST_CASE( static_object_partial_solution, fixture ) { my_class c( 42 ); MOCK_EXPECT( f ).once().with( &c ); f( &c ); mock::verify(); // verify the expectations before local objects are destroyed and before the fixture resets them }
Solution :
BOOST_FIXTURE_TEST_CASE( static_objects_solution, mock::cleanup ) // actually the library includes a ready to use fixture just like the one described { my_class c( 42 ); MOCK_EXPECT( f ).once().with( &c ); f( &c ); mock::verify(); }
Problem :
namespace { class base_class { public: virtual void method() = 0; }; class my_class { public: explicit my_class( base_class& ); void flush(); // repetitively calling this method will in turn call base_class::method at some point }; }
Solution :
#define BOOST_AUTO_TEST_MAIN #include <boost/test/auto_unit_test.hpp> #include <boost/lambda/lambda.hpp> #include <boost/thread.hpp> #include <turtle/mock.hpp> namespace { template< typename F > void check( bool& condition, F flush, int attempts = 100, int sleep = 100 ) { while( !condition && attempts > 0 ) { --attempts; boost::this_thread::sleep( boost::posix_time::milliseconds( sleep ) ); flush(); } } MOCK_BASE_CLASS( mock_base_class, base_class ) { MOCK_METHOD( method, 0 ) }; void set_bool(bool& b) { b = true; } } BOOST_AUTO_TEST_CASE( method_is_called ) { mock_base_class m; my_class c( m ); bool done = false; // when method is called it will set done to true // Note: Boost 1.57 introduced a bug preventing usage of the lambda with clang in C++98 // See: https://svn.boost.org/trac10/ticket/10785 #if defined(BOOST_CLANG) && (BOOST_VERSION >= 105700) MOCK_EXPECT( m.method ).once().calls( boost::bind(&set_bool, done) ); #else MOCK_EXPECT( m.method ).once().calls( boost::lambda::var( done ) = true ); #endif check( done, boost::bind( &my_class::flush, &c ) ); // just wait on done, flushing from time to time }
Problem :
namespace { class base_class { public: virtual void method( int value ) = 0; }; class my_class { public: explicit my_class( base_class& ); void process(); // the processing will call 'method' two times with the same value, but we don't know what value beforehand }; }
Solution :
#define BOOST_AUTO_TEST_MAIN #include <boost/test/auto_unit_test.hpp> #include <turtle/mock.hpp> namespace { MOCK_BASE_CLASS( mock_base_class, base_class ) { MOCK_METHOD( method, 1 ) }; } BOOST_AUTO_TEST_CASE( method_is_called_two_times_with_the_same_value ) { mock_base_class mock; my_class c( mock ); int value; MOCK_EXPECT( mock.method ).once().with( mock::retrieve( value ) ); // on first call retrieve the value, this expectation takes precedence because it can never fail MOCK_EXPECT( mock.method ).once().with( boost::cref( value ) ); // on second call compare the previously retrieved value with the newly received one c.process(); }
Problem :
#include <boost/function.hpp> namespace { class base_class { public: virtual void method( const boost::function< void( int ) >& functor ) = 0; }; void function( base_class& ); // the function will call 'method' with a functor to be applied }
Solution :
#define BOOST_AUTO_TEST_MAIN #include <boost/test/auto_unit_test.hpp> #include <boost/bind/apply.hpp> #include <turtle/mock.hpp> namespace { MOCK_BASE_CLASS( mock_class, base_class ) { MOCK_METHOD( method, 1 ) }; } BOOST_AUTO_TEST_CASE( how_to_invoke_a_functor_passed_as_parameter_of_a_mock_method ) { mock_class mock; MOCK_EXPECT( mock.method ).calls( boost::bind( boost::apply< void >(), _1, 42 ) ); // whenever 'method' is called, invoke the functor with 42 function( mock ); }
Problem :
#define BOOST_AUTO_TEST_MAIN #include <boost/test/auto_unit_test.hpp> #include <turtle/mock.hpp> #include <iostream> namespace { class my_class { public: explicit my_class( int data ) : data_( data ) {} int data_; }; std::ostream& operator<<( std::ostream& os, const my_class& c ) // my_class is serializable to an std::ostream { return os << "my_class( " << c.data_ << " )"; } MOCK_CLASS( my_mock ) { MOCK_METHOD( method, 1, void( const my_class& ) ) // how to simply write a custom constraint ? }; }
Solution :
#include <boost/lexical_cast.hpp> namespace // in the same namespace as 'my_class' { bool operator==( const my_class& actual, const std::string& expected ) // the first part of the trick is to compare to a string { return boost::lexical_cast< std::string >( actual ) == expected; } } // mock BOOST_AUTO_TEST_CASE( method_is_called ) { my_mock mock; MOCK_EXPECT( mock.method ).once().with( "my_class( 42 )" ); // the second part of the trick is to express the constraint as a string mock.method( my_class( 42 ) ); }