Hello. I finally finished writing the fcfTest unit testing library: https://github.com/fcf-framework/fcfTest
Until now, the library consisted of just a single macro; however, it now fully implements all the necessary functionality.
Its primary distinguishing feature lies in the use of a single assertion macro for all tests - a capability made possible by the fact that the library is written in C++. Furthermore, integrating it requires nothing more than a single header file.
The library supports independent command-line processing, allows for specifying the test execution order, and most importantly - supports a hierarchical test structure organized into three levels: Section -> Group -> Test. It can also be compiled as a standalone DLL.
Additionally, the library includes a simple logger (fcf::NTest::Duration::err() … fcf::NTest::log() … fcf::NTest::trc()) and a class for measuring execution duration (fcf::NTest::Duration).
The main FCF_TEST macro - suitable for all scenarios:
It allows for writing complex checks that include variable state monitoring.
FCF_TEST(a==15, a);
And in the terminal, you will see:
Test error: a == 15 [FILE: DIR_PATH/main.cpp:LINE]
Values:
a: 1
The first parameter is a computed verification expression, while all other parameters are observed variables.
Example
Next follows the main example from this library; the accompanying comment explains the core mechanics of its operation.
#include <vector>
#include <cmath>
// It is necessary to define the `FCF_TEST_IMPLEMENTATION` macro so that the
// implementations are exposed when the header file is included.
// If the `fcfTest/test.hpp` file is included multiple times within a project,
// this macro should be defined in only one `.cpp` file.
//
// When working with DLLs, you must define both the `FCF_TEST_IMPLEMENTATION`
// and `FCF_TEST_EXPORT` macros within the main library that exports
// the functions; conversely, in libraries that import these functions,
// you need to define only the `FCF_TEST_IMPORT` macro.
#define FCF_TEST_IMPLEMENTATION
#include <fcfTest/test.hpp>
// --- Test Declarations ---
FCF_TEST_DECLARE("Math" /*PART NAME*/,
"BasicArithmetic" /*GROUP NAME*/,
"Addition" /*TEST NAME*/) {
// We create an object to measure execution duration
// over 10,000 iterations.
fcf::NTest::Duration bench(10000);
// Set the starting time point for measuring execution time.
bench.begin();
for(size_t i = 0; i < bench.iterations(); ++i) {
int a = 2;
int b = 3;
// Performing a check of the unit test execution.
FCF_TEST(a + b == 5, a, b);
}
// We set the final time point for measuring execution time.
bench.end();
// Outputting the execution time measurement result at the 'info' logging level.
fcf::NTest::inf() << " Itertion count: " << bench.iterations() << std::endl;
fcf::NTest::inf() << " Total: " << bench.totalDuration().count() << " ns" << std::endl;
fcf::NTest::inf() << " Avg: " << bench.duration().count() << " ns" << std::endl;
}
FCF_TEST_DECLARE("Math" /*PART NAME*/,
"BasicArithmetic" /*GROUP NAME*/,
"Subtraction" /*TEST NAME*/) {
// We create an object to measure execution duration
// over 10,000 iterations.
fcf::NTest::Duration bench(10000);
// We perform the task 10,000 times.
bench([](){
int a = 10;
int b = 4;
// Performing a check of the unit test execution.
FCF_TEST(a - b == 6, a, b);
});
// Outputting the execution time measurement result at the 'info' logging level.
fcf::NTest::inf() << " Itertion count: " << bench.iterations() << std::endl;
fcf::NTest::inf() << " Total: " << bench.totalDuration().count() << " ns" << std::endl;
fcf::NTest::inf() << " Avg: " << bench.duration().count() << " ns" << std::endl;
}
FCF_TEST_DECLARE("Vector" /*PART NAME*/,
"SizeCheck" /*GROUP NAME*/,
"EmptyVector" /*TEST NAME*/) {
std::vector<int> v;
FCF_TEST(v.size() == 0, v.size());
}
// --- Order Registration ---
// Run Math tests before Vector tests
FCF_TEST_PART_ORDER("Math", 1);
FCF_TEST_PART_ORDER("Vector", 2);
// Run "BasicArithmetic" group first within Math part
FCF_TEST_GROUP_ORDER("BasicArithmetic", 1);
// Run Addition test first
FCF_TEST_TEST_ORDER("Addition", 1);
int main(int a_argc, char* a_argv[]) {
// Use CRM_RUN for standard execution
bool error;
fcf::NTest::cmdRun(a_argc, (const char**)a_argv, fcf::NTest::CRM_RUN, &error);
return error ? 1 : 0;
}
If you launch this application, you will see the following report:
Performing the test: "Math" -> "BasicArithmetic" -> "Subtraction" ...
Performing the test: "Math" -> "BasicArithmetic" -> "Addition" ...
Performing the test: "Vector" -> "SizeCheck" -> "EmptyVector" ...
All tests were completed. Number of tests: 3
However, if you specify the logging level parameter beforehand:
$ ./full-example --test-log-level inf
Then the result will be supplemented with information regarding execution speed.
Performing the test: "Math" -> "BasicArithmetic" -> "Subtraction" ...
Itertion count: 10000
Total: 65652 ns
Avg: 6 ns
Performing the test: "Math" -> "BasicArithmetic" -> "Addition" ...
Itertion count: 10000
Total: 77750 ns
Avg: 7 ns
Performing the test: "Vector" -> "SizeCheck" -> "EmptyVector" ...
All tests were completed. Number of tests: 3
Help on the full set of commands can be obtained by passing the command-line parameter --test-help
$ ./full-example --test-help
Test options:
--test-run - Run tests
--test-list - Displays a list of all tests
--test-part PART_NAME - Run only tests from the part. The parameter can be used multiple times
--test-group GROUP_NAME - Run only tests from the group. The parameter can be used multiple times
--test-test TEST_NAME - Run only this test. The parameter can be used multiple times
--test-log-level LEVEL - Logging level (VALUES: off, ftl, err, wrn, att, log, inf, dbg, trc)
--test-help - Help message
Top comments (0)