A generator of mock classes for C/C++ unit testing
Project description
Deride
Deride (pronounced /de'ride/) is a tool which produces a mock for a C/C++ file:
it analyses the input .h
header file using clang and
generates a new .cpp
file containing the implementation of the mock object
and a .h
header file which can be used to interface with the mock object.
For each method in the input class Deride generates a method in the mock controller which can be used to install a callback to be invoked whenever the original method is invoked.
NOTE: Deride is still a work-in-progress, has not been tested but on a handful of files, and is likely to crash on non-trivial input (in any case, should encounter such issues, please file a bug). It may be that the generated files are not usable as-is, but need manual editing.
Example usage
deride --pattern MyClass my_class.h
This will generate two files, mock_my_class.h
and mock_my_class.cpp
, which
you should link to your unit tests instead of the real MyClass implementation:
these files will provide a mock implementation of MyClass which you can control
and inspect in your tests.
For example, if horse.h
contains this class:
class HorsePrivate;
class Horse {
public:
Horse(const std::string &name);
Horse(Horse &&other);
virtual ~Horse();
std::string name() const;
void jump();
float jumpHeight() const;
void setColor(const std::string &colorName);
std::string color() const;
private:
HorsePrivate *d_ptr;
};
once Deride has run it will have created mock_horse.h
containing a
MockHorse
object having these methods:
class MockHorse {
// ...
static void onConstructorCalled(std::function<void(const std::string &)> callback);
static void onConstructorCalled(std::function<void(Animals::Horse &&)> callback);
void setNameResult(std::string result):
void setJumpHeightResult(float result);
void setColorResult(std::string result);
void onNameCalled(std::function<std::string()> callback);
void onJumpCalled(std::function<void()> callback);
void onJumpHeightCalled(std::function<float()> callback);
void onSetColorCalled(std::function<void(const std::string &)> callback);
void onColorCalled(std::function<std::string()> callback);
// ...
};
That is, for each method returning a value Deride generates a method called
set<method>Result()
which allows you to inject the desired result. In
addition to that, you can register a callback to be invoked every time that a
method is called. This allows you both to monitor how many times a method is
called (and with what params) and to provide its implementation; please note
that if you use the callback mechanism than the return value from the callback
will be used as the function's return value, instead of the one set via
set<method>Result()
).
Creating a mock object
So, how do you get a mock object? There are a couple of ways, and which one you use depends on the way that the target objects are instantiated:
-
If you expect only an instance of the object to get instantiated, then you can safely create the mock object on the stack as soon as your test starts:
MockHorse mock; mock.onConstructorCalled([](const std::string &name) { // This code is executed when the tested code creates a Horse std::cout << "A horse has been created: " << name; }); mock.onNameCalled([]() { ... });
-
If the test code creates several instances of the Horse class (well, you can do it even for a single one, but this method is especially useful when there are many of them), you can create a mock object for a given Horse instance by calling the
mockFor()
method:run_my_test_code(); // somehow, you get a handle to a `Horse*` in the variable `horse`. Then you // can do: MockHorse *mock = MockHorse::mockFor(horse); mock->onNameCalled([]() { ... });
-
And if you don't even have a way to retrieve a handle for the
Horse
objects, you can call thelatestInstance()
method:run_my_test_code(); // Get a mock for the latest (newest) instance of the Horse class created MockHorse *mock = MockHorse::latestInstance(); mock->onNameCalled([]() { ... });
-
Using a combination of the first solution and the previous one, it's possible to handle more complex cases (see
examples/many-mocks/test_program.cpp
for a full example):Mock *mockTom; Mock *mockDick; Mock *mockHarry; Animals::MockHorse::onConstructorCalled([&](const std::string &name) { std::cout << "Horse instantiated: " << name << std::endl; if (name == "Tom") { mockTom = Mock::latestInstance(); } else if (name == "Dick") { mockDick = Mock::latestInstance(); } else if (name == "Harry") { mockHarry = Mock::latestInstance(); } });
Mocking C libraries
Deride can also mock plain C libraries, but for convenience the generated mock will still be a C++ object, so that one can use lambda functions to register the monitoring callbacks.
A single Mock
file will be generated, containing the on<method>Called()
registration functions and (for functions returning a value) the
set<method>Return()
setters.
The small project under examples/lmdb/
shows how to generate and use a mock
object for the C lmdb library.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.