There’s a class under test which currently accepts a unique_ptr< Interface >&& in it’s constructor, to express that it wants to take single ownership of an Interface implementation. Problems arise when wanting to test this class using a mocked Interface though: the mocking framework (HippoMocks) gives me only Interface* which I do not own, hence cannot delete.
I had the same problem before when testing classes taking const shared_ptr< Interface >& as arguments, but fixed that by providing a custom no-op deleter:
template< class T >
void NoDelete( T* )
{
}
//create a shared_ptr without effective deleter
template< class T >
std::shared_ptr< T > mock_shared( T* t )
{
return std::shared_ptr< T >( t, NoDelete< T > );
}
Interface* iface = mocks.GetMeAMock< Interface >();
DoStuffWithSharedPtrOfInterface( mock_shared< Interface >( iface ) );
A similar fix for unique_ptr doesn’t really work out because the deleter is a template argument:
template< class T >
struct NoDelete
{
void operator ()( T* )
{
}
};
//oops this is totally useless since std::unique_ptr< T, NoDelete< T > >
//is not quite the same type as std::unique_ptr< T >
template< class T >
std::unique_ptr< T, NoDelete< T > > mock_unique( T* t )
{
return std::unique_ptr< T, NoDelete< T > >( t, NoDelete< T >() );
}
Is there a workaround for this? Or should I not be using unique_ptr here in the first place?
update
I gave this a go; should work but sizeof( ptr ) is now 8, hard to tell what impact that has.
//use CustomUniquePtr::type instead of uniqe_ptr
template< class T >
struct CustomUniquePtr
{
typedef typename std::unique_ptr< T, void (*) ( T* ) > type;
}
//use everywhere
template< class T >
CustomUniquePtr< T >::type make_unique( T* p )
{
return CustomUniquePtr< T >::type( p, Delete< T > );
}
//use when mocking, doe not delete p!
template< class T >
CustomUniquePtr< T >::type mock_unique( T* p )
{
return CustomUniquePtr< T >::type( p, NoDelete< T > );
}
Hippomock already provides a solution to this problem. If you have an interface with a virtual destructor then all you need to do is to register an expectation for the destructor. The mock is not destroyed by a call to its destructor as it is a mock destructor, but an expectation for the call to the destructor must be set.