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.

Commentos

There's also webmention support.