Mock Objects for C++, Part 3
Tuesday 4 November 2008 - Filed under Code
This is part 3 of an ongoing series on implementing automatic mock object support for C++, by breaking pretty much every rule in the book.
In Part 1, we made fake vtables, which we could manipulate however we pleased, and callers were none the wiser.
In Part 2, we pulled apart the virtual dispatch code generated by Visual C++, and derived a function which yields a vtable index for a pointer-to-member.
Next, we’re going to pull all these pieces together so we can add expectations on methods.
I won’t cover all the gory details, since the actual library uses a huge pile of templates to get these features to work, but a quick overview of the ideas shouldn’t be too insane.
So, we had our desired syntax last time:
expect( foo, &IFoo::Bar ).once().returns( 42 );
We’ll end up supporting more than just once() and returns() here – immediately, other options like atLeast(), atMost(), between(), never() come to mind.
The expect() function creates a new expectation, sneaking in type information through inferred type parameters, and then returns us a throwaway object which can be used to alter the properties of the expectation – add an arg filter, set a return value, set expected call counts, etc.
It ends up looking something like this (the real version has some extra stuff, in order to give reasonable error messages about where expectations were set):
template< typename I, typename F >
class Expectation
{
expect_t * e;
Vtbl * v;public:
Expectation( expect_t * e, Vtbl * v )
: e(e), v(v) {}Expectation once()
{
e->minCalls = e->maxCalls = 1;
return *this;
}// other members omitted
};template< typename I, typename F >
Expectation<I,F> expect( I* obj, F func )
{
size_t i = get_function_index(
evil_cast( func ) );Vtbl * v = (Vtbl *)obj;
expect_t * e = new expect_t(
v->expectations[ i ],
0, -1, i ); // `any # of calls`,
// chain to previous
v->expectations[ i ] = e;
return Expectation<I,F>( e, v );
}template< typename I >
bool verify( I* obj )
{
Vtbl * v = (Vtbl *)obj;
bool success = true;
for( size_t i = 0; i < MAX_VTABLE_SLOTS;
i++ )
{
expect_t * e = v->expectations[i];
while(e)
{
// isMet() prints errors
if (!e->isMet())
success = false;
e = e->next;
}
}
return success;
}
(Where expect_t is a type-ignorant runtime expectation, and the Vtbl class contains an array of expect_t *, parallel to the vtable itself.)
Next time, we’ll look at having the compiler help us generate functions to check the calls, based on the type F we get from the expect() call.
2008-11-04 » admin
5 November 2008 @ 4:13 am
[...] Chris on Software Not quite a Yegge long. « Mock Objects for C++, Part 3 [...]