Download News Support Project

PrevUpHomeNext

Limitations

No support for unicode logging
Literal 0 cannot be used as null pointer in constraints
Non-virtual methods cannot be mocked
Template methods cannot be mocked
Template base class methods cannot be mocked without specifying the signature
Protected and private virtual methods cannot be mocked without specifying the signature
Methods with a throw specifier cannot be mocked
Compilers without support for variadic macros fail on commas in MOCK_BASE_CLASS
Warning C4505: '...' : unreferenced local function has been removed
Warning C4301: '...': overriding virtual function only differs from '...' by const/volatile qualifier
Warning C4267: 'argument' : conversion from 'size_t' to 'unsigned int', possible loss of data
Using C++11 lambda as constraints requires decltype compiler support

This section lists the library known limitations.

There is no support for unicode logging mainly because Boost.Test does not support it either.

Given :

class base
{
public:
    virtual void method( int* i ) = 0;
};

MOCK_BASE_CLASS( mock_base, base )
{
    MOCK_METHOD( method, 1 )
};

The following code does not compile :

BOOST_AUTO_TEST_CASE( literal_zero )
{
    mock_base m;
    MOCK_EXPECT( m.method ).with( mock::equal( 0 ) ); // this fails
    MOCK_EXPECT( m.method ).with( 0 );                // this fails too !
}

This is due to the fact that the library uses templates pretty heavily, and literal 0 is considered as an int when instantiating a template function.

A workaround is :

MOCK_EXPECT( m.method ).with( mock::equal< int* >( 0 ) ); // this compiles

However a somewhat better solution would be :

MOCK_EXPECT( m.method ).with( mock::negate );

or with C++11 nullptr support :

MOCK_EXPECT( m.method ).with( nullptr );

Given :

class base
{
public:
    void method() // the method is not virtual
    {}
};

the following code compiles but will not work as expected :

MOCK_BASE_CLASS( mock_base, base )
{
    MOCK_METHOD( method, 0 )
};

The mock object will never be exercised because the library relies on polymorphism to hook the calls.

There is no other solution than to refactor the production code, the most simple being to change the method to virtual.

Given :

class concept
{
public:
    template< typename T >
    void method( T t )
    {}
};

template< typename T >
void function_under_test( T t ) // T is supposed to model the previous concept
{
    t.method( 42 );
    t.method( "string" );
}

writing a mock object modeling 'concept' requires to list all the possible versions of 'method' :

MOCK_CLASS( mock_concept )
{
    MOCK_METHOD( method, 1, void( int ), method_int )
    MOCK_METHOD( method, 1, void( const char* ), method_string )
};

However if one or more template parameters must be explicitly specified as in :

class concept
{
public:
    template< typename T >
    T create()
    {
        return T();
    }
};

template< typename T >
void function_under_test( T t ) // T is supposed to model the previous concept
{
    t.template create< int >();
    t.template create< std::string >();
}

delegate methods must be manually added :

MOCK_CLASS( mock_concept )
{
    template< typename T >
    T create();

    MOCK_METHOD( create_int, 0, int(), create_int )
    MOCK_METHOD( create_string, 0, std::string(), create_string )
};

template<>
int mock_concept::create< int >()
{
    return create_int();
}
template<>
std::string mock_concept::create< std::string >()
{
    return create_string();
}

While still somewhat possible, mocking a template method can indeed prove a bit cumbersome.

Given :

template< typename T >
class base
{
public:
    virtual ~base()
    {}

    virtual void method() = 0;
};

the following code does not compile :

template< typename T >
MOCK_BASE_CLASS( mock_base, base< T > )
{
    MOCK_METHOD( method, 0 ) // this fails
};

A workaround would be to add the signature to MOCK_METHOD :

template< typename T >
MOCK_BASE_CLASS( mock_base, base< T > )
{
    MOCK_METHOD( method, 1, void() )
};

Given :

class base
{
protected:
    virtual void method_1() = 0;
private:
    virtual void method_2() = 0;
};

the following code does not compile :

MOCK_BASE_CLASS( mock_base, base )
{
    MOCK_METHOD( method_1, 0 ) // this fails because a function pointer on 'base::method_1' is not allowed
    MOCK_METHOD( method_2, 0 ) // this fails because 'method_2' is not visible
};

A workaround would be to add the signature to MOCK_METHOD :

MOCK_BASE_CLASS( mock_base, base )
{
    MOCK_METHOD( method_1, 0, void() )
    MOCK_METHOD( method_2, 0, void() )
};

Given :

struct base_class
{
    virtual ~base_class()
    {}

    virtual void method() throw ();
};

the following code does not compile :

MOCK_BASE_CLASS( mock_class, base_class )
{
    MOCK_METHOD( method, 0 ) // this fails because of the throw specifier
};

A workaround would be to write a proxy member function :

MOCK_BASE_CLASS( mock_class, base_class )
{
    void method() throw ()
    {
        method_proxy();
    }
    MOCK_METHOD( method_proxy, 0, void(), method )
};

For compilers without support for variadic macros given :

template< typename T1, typename T2 >
struct my_base_class
{};

the following code does not compile :

MOCK_BASE_CLASS( my_mock, my_base_class< int, int > ) // this fails because the pre-processor believes the macro to be called with 3 arguments
{};

One workaround is :

typedef my_base_class< int, int > my_base_type;

MOCK_BASE_CLASS( my_mock, my_base_type )
{};

Of course this is not always possible, as in :

template< typename T1, typename T2 >
MOCK_BASE_CLASS( my_mock, my_base_type< T1, T2 > )
{};

Another workaround would make use of Boost.Preprocessor :

template< typename T1, typename T2 >
MOCK_BASE_CLASS( my_mock, my_base_class< T1 BOOST_PP_COMMA() T2 > )
{};

Actually BOOST_PP_COMMA implementation is quite trivial, being only :

#define BOOST_PP_COMMA() ,

Finally another workaround would be to not use the macro at all :

template< typename T1, typename T2 >
struct my_mock : my_base_class< T1, T2 >, mock::object
{};

Note that Boost.IdentityType is of little help here because the type is by essence very often abstract, which doesn't work well for some compilers (e.g. gcc).

Example :

warning C4505: 'base::[thunk]: __thiscall base::`vcall'{0,{flat}}' }'' : unreferenced local function has been removed

This seems to be a random bug with some versions of the Microsoft Visual Studio compiler.

The only known workaround is to disable the warning with a pragma :

#pragma warning( disable: 4505 )

Example :

warning C4301: '`anonymous-namespace'::base::method': overriding virtual function only differs from '`anonymous-namespace'::base::method' by const/volatile qualifier

Given :

class base
{
public:
    virtual void method( const int ) = 0;
};

the following code produces this warning with some versions of the Microsoft Visual Studio compiler :

MOCK_BASE_CLASS( mock_base, base )
{
    MOCK_METHOD( method, 1 )                    // this produces the warning
    MOCK_METHOD( method, 1, void( const int ) ) // forcing the signature will not help, this produces the warning too !
};

The problem is that the 'const' is actually not part of the function signature and therefore cannot be introspected.

The first workaround would be to remove the 'const' all together.

This is more sensible than it first sounds, after all the 'const' is useless in this situation, indeed the following compiles, links and is valid C++ :

class derived : public base
{
public:
    virtual void method( const int );
};

void derived::method( int )
{}

Otherwise another workaround would be to provide a proxy method :

MOCK_BASE_CLASS( mock_base, base )
{
    void method( const int i )
    {
        method_stub( i );
    }
    MOCK_METHOD( method_stub, 1, void( int ), method )
};

Compiling under Microsoft Visual Studio with the /Wp64 flag produces this warning at various locations in the library code.

This is actually a bug in the compiler, for more information see incorrect-warning-c4267.

The technique used in order to detect whether a constraint is a C++11 lambda or not is based on decltype, which means the library can fail to detect a lambda in case the compiler does not support it.


PrevUpHomeNext