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

If you're not sure about the file name format, learn more about wheel file names.

deride-0.2-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file deride-0.2-py3-none-any.whl.

File metadata

  • Download URL: deride-0.2-py3-none-any.whl
  • Upload date:
  • Size: 16.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.10.6

File hashes

Hashes for deride-0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ce3181ce62663d23e63258a2eae527c1156f11ab726e6342f6b39c7ff1204951
MD5 743c002ecbde2bed305cac81527dcdf8
BLAKE2b-256 eacf5fbf948f5a1f60e79397e02e13e1cc0e19552c452ae7cf558ca8cf09d441

See more details on using hashes here.

Supported by

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