Download News Support Project

PrevUpHomeNext

Patterns

Managing static mock objects
Waiting for an asynchronous call
Retrieving an argument to use in a later constraint
Invoking a functor received as parameter
Quickly writing a custom constraint for a serializable type

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 )
    };
}

BOOST_AUTO_TEST_CASE( method_is_called )
{
    mock_base_class m;
    my_class c( m );
    bool done = false;
    MOCK_EXPECT( m.method ).once().calls( boost::lambda::var( done ) = true ); // when method is called it will set done to true
    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( 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 mock // it could also be in the namespace of '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 ) );
}


PrevUpHomeNext