-
February 9th, 2012, 07:08 PM
#1
Mocking a class whose constructor is called by a "black box"
I am trying to isolate for the purpose of unit testing class A which uses internally class Socket (a facade for socket function calls). The tools I am using are Visual Studio 2005, Visual Assert using the WinUnit interface, and hippomocks, but it is unimportant.
For the purpose of unit testing, class A is a black box, it creates Socket objects on a need-to basis, most importantly a single Socket object at a time, and it doesn't have any constructors that take Socket references or pointers, so I cannot simply pass a mock created by the mocking framework to an A constructor.
The solution I came up with incorporates elements of the NVI (nonvirtual interface) and Pimpl patterns and works as follows: Define an interface (all methods pure virtual) ISocket class (required by my mocking framework) that provides the same interface as the real Socket class, and then define a mock Socket class for testing purposes that contains a static ISocket Pimpl and non-virtual pass-throughs. Additionally, the mock Socket class contains static queue<string>'s of read and write "buffers" that allow me, through the nonvirtual read and write pass-throughs, to set up the messages that the socket will "receive" during the unit test, and to inspect messages "sent" by the socket at the end of the unit test. I am not happy about the Pimpl and "buffers" being static, but I don't see any way around it, besides using global variables. Either way, I can't have a unit test that involves two or more A objects. More importantly, if any A method created more than one Socket object at a time, this whole scheme would be a non-starter.
I would like to know if anyone here can think of or has employed in the past a better approach. Any and all feedback will be appreciated, thanks.
Note that the code below is not the actual code---for brevity I combined the .h and .cpp file contents whereas in reality class definitions (.h) and their static variables and methods definitions (.cpp) are separate.
Code:
class ISocket
{
public:
int read ( void *, int, bool, int ) = 0;
int write ( void const *, int ) = 0;
};
class Socket
{
public:
// pass-throughs
int read ( void * const buf,
int const size,
bool const blocking,
int const flags )
{
int const err = this->pimpl_->read( buf, size, blocking, flags );
strcpy_s( (char*)buf, size, Socket::reads_.front().c_str() )
Socket::reads.pop_front();
return err;
}
int write ( void const * const buf, int const size )
{
Socket::writes_.push_back( (char*)buf );
return this->pimpl_->write( buf, size );
}
public:
static ISocket * pimpl_;
static queue<string> reads_;
static queue<string> writes_;
}
BEGIN_TEST(foo_FirstCall_ReturnFalse)
{
MockRepository mocks;
Socket::pimpl_ = mocks.InterfaceMock<ISocket>();
Socket::reads_.clear();
Socket::writes_.clear();
Socket::reads_.push_back( string("input msg 1") );
Socket::reads_.push_back( string("input msg 2") );
A a;
bool err = a.foo();
ASSERT_EQUAL(false, err,
"return value on first call is not false");
ASSERT_TRUE(1 <= Socket::writes_.size(),
"no messages were sent");
ASSERT_EQUAL(1, Socket::writes_.size(),
"more than one message was sent");
ASSERT_EQUAL("output msg", Socket::writes_.front(),
"wrong output message sent");
}
END_TEST
Last edited by natskvi; February 9th, 2012 at 07:42 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|