dock: simple C ++ code unit testing library

Although there are already libraries for unit testing of C ++ code, for example, Google Test or Bandit , butthey are not written by mehere, in my opinion, it is somehow overcomplicated in comparison with the same JS. There you just do it, for example, npm i mocha assert --save-devand you can start writing tests, but here you need to do it with pens, and in the case of gtestit, also assemble cmakeit using it. Bandit connects simply, but does not know how to serialize results to some data format, gtestit does, but it needs to be collected separately. And I do not want to choose "either this or that." I needed to make a convenient and simple tool for my tasks. I wanted to get a simple library without dependencies, header-only, for several files, which can be easily and quickly connected to my project, it is convenient to make changes to it (if necessary). But, most importantly, I wanted to receive convenient, machine-readable reports, and not only in stdout(orxml, as in gtest), but also in any other format that I want. Further under the cut.


As I wrote above, the dock header-only library , which means its connection is as simple as possible:


#include 
#include 
using namespace dock;
int main() {
    core().run();
    return 0;
}

When building, for example, in gcc, you need to transfer only the path to the library folder and specify the C ++ 14 language standard. I intentionally do this because I am writing new projects on a fresh standard, and I have my own libraries ready to support the old ones.


The description of the tests is also made extremely simple:


using namespace dock;
Module(u8"Some module 1", [](DOCK_MODULE()) {
    Test(u8"Some test 1", []() {
        uint8_t value = 0x10;
        uint8_t expectedValue = 0x10;
        Assert::isEqual(value, expectedValue);
    });
    Test(u8"Some test 2", []() {
        uint8_t value = 0x10;
        uint8_t expectedBorder = 0x20;
        Assert::isLess(value, expectedBorder);
    });
});
Module(u8"Some module 2", [](DOCK_MODULE()) {
    Test(u8"Some test 1", []() {
        Assert::isTrue(true);
    });
    Test(u8"Some test 2", []() {
        Assert::isTrue(false);
    });
});

For convenience, tests are grouped into modules. They pass an object inside which tests are directly described. Tests have approximately the same syntax, only a functional object without parameters. So far I have not done a check for uniqueness of the name of the module or test, because it was not critical.std::function


"Library" Assertcontains a simple set of methods isTrue, isEquals, isGreater, isLess, which by default can compare objects in terms of operators ==, >or <. If there are no operators, then you can pass the comparison function at the end to a parameter (for example, in the form of a lambda).


static void isTrue(std::function fcn);
template
static void isEqual(const T a, const T b, std::function compareFcn = defaultEqualsFunction);
template
static void isGreater(const T a, const T b, std::function compareFcn = defaultGreaterFunction);
template
static void isLess(const T a, const T b, std::function compareFcn = defaultLessFunction);

And now just what I needed: a convenient conversion of the test results into the required data format. For starters, I just want to work with the statistics of the project, watch the dynamics of the tests and similar things, and it’s convenient for me to do this on JS. Therefore, the first format I needed was JSON. There are already three ready-made serializers in the repository: in JSON, in plain text and output to the console with highlighting. Using serializers is very simple:


nlohmann::json outJson;
JsonSerializer serializer(outJson, 4);
core().run();
core().collect(serializer);
std::cout << serializer << std::endl;

And the serializer interface itself is as follows:


class ResultSerializer {
public:
    virtual ~ResultSerializer() = default;
    virtual void            serialize(std::vector& results) = 0;
    virtual std::string     toString() const = 0;
    friend std::ostream&    operator<<(std::ostream& os, ResultSerializer& s);
};

Those. we can output the result anywhere, substitute only std::ostreamthat. The logic of the serializer is as follows:


  • We pass the serializer to the engine through collect()and it calls a method serialize()with a vector of results.
  • The operator <<invokes a method toString()that returns a string in std::ostream.
    You can do two options: either when calling serialize(), we immediately create the desired line, and then we either simply return it or save a link to the results and generate the output directly when it is output to ostream. In any case, there remains freedom of movement - the engine gives out simply , but what your business is to do :).std::vector

The license is free (MIT), because I do not mind and will be pleased to see its use. The termcolor and JSON for Modern C ++ libraries were used for serializers , but you can safely remove them along with unnecessary serializers.


Also popular now: