Skip to main content

A generator of mock classes for C/C++ unit testing

Project description

pipeline status coverage report

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:

  1. 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([]() { ... });
    
  2. 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([]() { ... });
    
  3. And if you don't even have a way to retrieve a handle for the Horse objects, you can call the latestInstance() method:

    run_my_test_code();
    // Get a mock for the latest (newest) instance of the Horse class created
    MockHorse *mock = MockHorse::latestInstance();
    mock->onNameCalled([]() { ... });
    
  4. 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.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

deride-0.4-py3-none-any.whl (16.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page