Mocking support in CppUTest
I've added mocking support to the CppUTest C/C++ unit testing framework. Id like to clarify why I've added it, why I've added it the way it is and how you can use it.
Why mocking?
I won't cover why mocking and state-based TDD vs interaction-based TDD. There are other wonderful descriptions of this. For example:
When working in C++, I mainly used state-based TDD with sometimes handcrafted mocks. I've stayed away from most mocking frameworks as I didn't like them much (though google mock is quite nice). Neither do I prefer interaction based over state based or the other way around, both are tools and are useful in different situations.
Why CppUTest mocking?
I always want to keep CppuTest small and clean. We (mainly me and James Grenning) have quite strict design guidelines for CppUTest. No major C++ features ought to be used, such as rtti, expection handling, templates, and STL. The main reason is that we work a lot for embedded software and embedded C++ compilers aren't that good. A smaller C++ set leads to greater and easier portability. To keep it simple, I've never really wanted to add mock support. But recently I've changed my mind and added it to the CppuTestExt (extensions) library.
Why did I changed my mind? Well... when working on a piece of code... I figured that an interaction-based approach would be best to test-drive it (lots of interaction, few state). I started to build manual mocks, as I always do in that situation, and was getting annoyed. Why was I getting annoyed? Exactly because I always do.... the same thing... and was getting bored by that. Therefore I decided to extract the things I do manually into mocking support.
But why not use one of the existing mocking frameworks like googlemock, mockpp, MockItNow, CMock? Important, because we don't want to re-invent the wheel again. My reasons for not using any of these:
- Need to use the same limited set of C++ as CppUTest
- No code generation
- No or very few macros
- Wanted to use it in CppUTest but don't want a dependency to other frameworks
All existing frameworks failed to meet the criteria (yeah, I know, the last criteria is unfair and per definition all frameworks will not meet that).
What were my major design criteria? Simplicity. Also instead of mocking automatically, I wanted to make manual mocking easier. Manual mocking won't go away in C++ and being able to do that with nearly the same ease as automatic mock generation would be a Good Thing. Also, the syntax needs to be easy and it ought to be possible to use the same mocking in C and C++.
In other words, the mocking support needs to be non-controlling and non-invasive.
There we have it. That's why I've build it... but how does it work?
How does it work?
The only class you really need to use it is MockSupport (in CppUTestExt/MockSupport.h). There are a lot more classes under the hood, but usually you don't need to use these (except for the MockFunctionCall interface).
Usually I just create one of the MockSupport and dump it in global static namespace in my test, something like:MockSupport mock;
(this is safe as the constructor/destructor don't do a thing)
In a test, if you except a call to a certain function, you do:mock.expectOneCall("functionName");
Then at the end of your test, you need to check that this happened with:mock.checkExpectations();
Thats basically all you need :) Oh, of course, you still need to make the actual call :P You can do this by putting a call to mock.actualCall inside the manual mock, like:mock.actualCall("functionName");
(yes, you still have to create a manual mock! That's the whole point :) Yes, you can let the manual mock do whatever you want it to do still too.
What about parameters?
How do we deal with parameters? Simple. In our expect call you write:mock.expectOneCall("functionName").withParameter("parameterName", 10);
Now the mocking support expects one parameter of value 10. In the actual call you put:mock.actualCall("functionName").withParameter("parameterName", parameter);
You can pass however many parameters, for example:mock.expectOneCall("functionName").withParameter("p1", 1).withParameter("p2", 2);
etc etc.
What about different types?
Also, same and easy. You add to the test:mock.expectOneCall("functionName").withParameterOfType("typeName", "parameterName", objectPtr);
And, you probably already guess the actual call:mock.actualCall("functionName").withParameterOfType("typeName", "parameterName", paraPtr);
The only problem is how to compare these types. For that, you'll need to create and install a Comparator. How? For example:class MyTypeComparator : public MockParameterComparator
{
public:
virtual bool isEqual(void* object1, void* object2)
{
return ((MyType*)object1)->field == ((MyType*)object2)->field;
}
virtual SimpleString valueToString(void* object)
{
return StringFrom(((MyType*)object)->field);
}
};
Then you create one and install it like:
mock.installComparator("TypeName", myTypeComaprator);
That's it for now. This covers for most of my own mocking needs, so I stopped. We want to keep things simple and clear, and this is simple and clean.
You want to have more examples? I added an example to the CppUTest/Examples. You can find it here
How to use it?
Right now, its on the trunk. So you need to grab the trunk. Its not in the zipped releases yet.
Also its part of CppUTest extensions. You can make them with:
make extensions
It will produce a library at:
lib/libCppUTestExt.a
What next?
The main functionality I wanted is there. I don't want to make it too complex, but gradually add some more. My current wishlist for the next weeks is:
- Support for return values
- Support for checking the right object is called
- A C interface
- Support for comparators that aren't just for types (like Matchers)
With those, I think the main mocking functionality I use is implemented.
I'd love feedback and bug reports (of course, there aren't any bugs :P)