Deride, a generator of mock objects for unit testing
If you have been writing C++ classes for mocking out your C or C++ dependencies, you know how tedious it is. I generally write small classes with just a handful of methods, so it's generally bearable, but when using third-party code I'm usually not that lucky. If the dependency is a C library this becomes especially tricky, both because they might be larger than what you can handle, and both because the lack of an object-oriented design might not offer you an easy solution to store the mock object data.
But fear no more, Deride is here!
I won't spend too many words describing it, since you can read its description
from the link above, where you will also find some example code. More examples,
by the way, can be found in the example/
folder in the code
repository, where you can see how it can be
used to mock both pure C++ and QObject
-based classes, and C libraries.
What is most important for me to say now, is that the project is in alpha state, meaning that I've tried it on a handful of header files only; it's highly likely that it will not work on many real-life scenarios, and if that happens I warmly invite you to inform me by filing a bug report providing the include file that was not properly processed.
I leave you with a short example of a unit test, written using Deride. The
class under test is called Stable
, and internally it uses objects of type
Horse
, that we decided to mock. We used Deride to generate the mocked
implementation and a MockHorse
class which can be used to control the mocked
objects. When building the test, we won't link against the original
horse.cpp
, but we'll only use the original horse.h
; the implementation will
be found in mock_horse.cpp
, generated by Deride. And in the corresponding
mock_horse.h
file we'll find the MockHorse
class with all the
on<method>Called()
hooks which we can use to install our callbacks (either to
reimplement the object behaviour, or to just be notified on when its methods
are called).
/* This MockHorse is the object created by Deride * | * | * \|/ * V */ using Mock = Animals::MockHorse; std::list<std::string> horseNames = { "Tom", "Dick", "Harry", }; /* We could use a vector, but let's be explicit */ Mock *mockTom; Mock *mockDick; Mock *mockHarry; int createdHorses = 0; // onConstructorCalled() is created by Deride and called when the mocked // Horse object is created Animals::MockHorse::onConstructorCalled([&](const std::string &name) { std::cout << "Horse instantiated: " << name << std::endl; createdHorses++; if (name == "Tom") { mockTom = Mock::latestInstance(); } else if (name == "Dick") { mockDick = Mock::latestInstance(); } else if (name == "Harry") { mockHarry = Mock::latestInstance(); } else { assert(false); // should not be reached } }); Stable stable; stable.createHorses(horseNames); /* It's at this point that the contructor callbacks we defined above will * have been called. Let's double-check that indeed that's the case. */ assert(createdHorses == 3); assert(stable.count() == 3); assert(mockTom != nullptr); assert(mockDick != nullptr); assert(mockHarry != nullptr); /* Prepare for mocking the jump; these methods are generated by Deride and * allow setting the return value for the corresponding jumpHeight() method * from the original Horse class. */ mockTom->setJumpHeightResult(1.5); mockDick->setJumpHeightResult(1.7); mockHarry->setJumpHeightResult(1.3); std::string highestJumper = stable.findHighestJumper(); assert(highestJumper == "Dick");
In my closing words I'd like to thank the Clang project, which Deride is using to parse and interpret the input files, and Jinja2, the templating engine used to generate the mock code.
Commenti
There's also webmention support.