Skip to main content

How to use the µTest++ Testing Framework

Rationale​

At its most basic level, the simplest testing approach requires no framework at all. A test for a C or C++ project can be written without any framework, perhaps using the standard assert() macro or a similar user-defined mechanism.

However, with this mechanism, failed assertions typically abort the test, and when dealing with multiple test cases containing numerous checks, it is preferable to obtain a complete report.

Testing frameworks address this need by providing convenient mechanisms for writing various checks and generating clear, structured reports.

The xPack Framework already includes ready-to-use support for several testing frameworks (GoogleTest, Catch2, Boost UT). Nevertheless, these frameworks tend to be quite demanding in terms of memory resources, and the learning curve required to master them can be rather steep.

Therefore, for embedded projects, a simpler solution with a smaller memory footprint was considered a valuable addition.

Overview​

The initial version of the µTest++ Testing Framework drew its primary inspiration from node-tap, with a strong emphasis on simplicity. Version 3.x was heavily influenced by Boost UT, from which it derived its function comparators, while version 4.x represented a complete redesign, drawing even more inspiration from node-tap by passing explicit references to parent objects into test and suite callbacks. Version 6.0 is the result of a systematic review of the API and of the implementation, which simplified the API, reworked the runner configuration, the test hierarchy, the checks, and the reports, and deprecated the overloaded operators, the literals, and the type wrappers in favour of the function comparators. For more details, see the History page.

Concepts and features​

  • The runner drives a test session: it holds the test suites, runs them, counts the passed and failed checks, reports the results, and returns the primary result of the test to the system as the process exit code.
  • A test suite is a named collection of test cases; for more complex applications, test cases are organised into test suites.
  • Test suites can reside in separate compilation units and automatically register themselves with the runner.
  • A test case comprises a sequence of checks (the test conditions of the Glossary), which are expectations or requirements — conditions anticipated to be true.
  • Checks are based on logical expressions, typically evaluating a result and comparing it to an expected value; each check either passes or fails.
  • For C++ projects, it is also possible to verify whether evaluating an expression results in an exception being thrown.
  • A failed expectation is reported and counted, and the test case continues.
  • Requirements are critical conditions that must be satisfied for the test case to proceed; a failed requirement stops the current test case, and the run continues with the next one.
  • The reporter writes the results on stdout, in TAP format by default: one line for each passed test case and, for the failed ones, the failed checks, with their location and, where possible, the values of their operands.
  • Optionally, the complete report is also written to a file (see Output file).

A test suite is deemed successful if there are no failed checks.

If all the test suites are successful, the test is successful, and this is indicated by an exit code of 0 (see Test result for the other codes).

Contents of this guide​

The guide is organised by the task of the reader:

  • Tutorial: the first tests, step by step, with the code and the output of each;
  • Writing checks: expectations, requirements, the function comparators, and the checks for strings, containers, and exceptions;
  • Organising tests: the runner, the top suite, test cases, and test suites;
  • Running tests: the command-line options, the reporters, and the colours;
  • Extending the framework: custom types, custom comparators, and the utility functions;
  • Embedded targets: memory footprint, stack usage, timings, the console output, and the builds without exceptions;
  • Migrating from earlier versions: the replacement of the deprecated operators, literals, and wrappers.

The exact declarations are in the API reference.

C API​

While µTest++ can be used to test C code, its implementation relies extensively on modern C++ features that cannot be replicated in C.

Consequently, there are no C equivalents for the C++ definitions provided by µTest++.