Micro (header-only) library for unit testing.
Library author: Piotr Likus
Created: 31/08/2020
Initial code based on blog post by Bastian Rieck, see: https://bastian.rieck.me/blog/posts/2017/simple_unit_tests/
This library requires C++11 or later and has been tested on Linux Mint 20 and other modern Linux distributions.
Key C++11 features used:
- Lambda expressions
auto
keyword for type deduction- Uniform initialization syntax (
{}
) std::enable_if
for SFINAEstd::chrono
for timingnullptr
andstd::nullptr_t
static_assert
for compile-time checksdecltype
for type deduction
The library is fully compatible with C++11, C++14, C++17, and C++20 standards.
https://github.com/vpiotr/utest
mkdir _build && cd _build
cmake ..
cmake --build .
# Run the demo
./bin/utest_demo
# Run the tests
ctest
# or
make test
mkdir _build && cd _build
cmake ..
cmake --build .
sudo cmake --install .
The project includes several helper scripts for common tasks:
rebuild.sh
- Clean and rebuild the entire project from scratchrun_demos.sh
- Execute all demo programs sequentially with detailed output and summaryrun_tests.sh
- Run all test executables with summary reporting (use--detailed
for full output)build_docs.sh
- Generate Doxygen documentation in HTML format
Usage examples:
# Quick rebuild
./rebuild.sh
# Run all demos with colored output
./run_demos.sh
# Run tests with summary only
./run_tests.sh
# Run tests with detailed output
./run_tests.sh --detailed
# Generate documentation
./build_docs.sh
UTEST_BUILD_DEMO
: Build the demo project (ON by default)UTEST_BUILD_TESTS
: Build the tests (ON by default)
Example with options:
cmake -DUTEST_BUILD_DEMO=OFF -DUTEST_BUILD_TESTS=ON ..
This library is fully compatible with C++11 and later standards. To use it in your project, ensure your compiler supports C++11:
# In your CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Or with compiler flags:
g++ -std=c++11 your_tests.cpp
clang++ -std=c++11 your_tests.cpp
The library leverages modern C++11 features for clean, expressive test code while maintaining compatibility with older codebases.
Simply copy include/utest.h
into your project and include it.
Add utest as a subdirectory in your CMake project:
add_subdirectory(path/to/utest)
target_link_libraries(your_project PRIVATE utest)
Or, if installed:
find_package(utest REQUIRED)
target_link_libraries(your_project PRIVATE utest::utest)
This library is header-only solution, so you just need to include single header file utest.h
in your test runner code.
Create a runner application executing single function which executes all tests plus adds prolog (UTEST_PROLOG) and epilog (UTEST_EPILOG) code.
Each test must be prepared as a function called test_aaa
and must be registered using UTEST_FUNC
macro.
Example complete test runner:
#include "utest.h"
void test_assert_equals() {
int a{42};
UTEST_ASSERT_EQUALS(a, 42);
}
int run_all_tests() {
UTEST_PROLOG();
UTEST_FUNC(assert_equals);
UTEST_EPILOG();
}
int main() {
return run_all_tests();
}
See tests in "tests" subdirectory and the "demo" directory for more examples.
The demo project demonstrates various aspects of the library across multiple executable demos:
- File:
demo_core_features.cpp
- Executable:
./bin/utest_demo
- Features demonstrated:
- Basic assertions (equality, true/false)
- Assertions with custom error messages
- Exception testing with lambda functions
- Complex test scenarios with collections and vectors
- String comparisons and container operations
- File:
demo_unicode_checkmarks.cpp
- Executable:
./bin/demo_unicode_checkmarks
- Features demonstrated:
- Default Unicode checkmark output format
- Test grouping with
UTEST_FUNC_DEF2
syntax - Multiple grouped test scenarios
- Various assertion types (GT, GTE, NEQ, etc.)
- File:
demo_verbose_mode.cpp
- Executable:
./bin/demo_verbose_mode
- Features demonstrated:
- Verbose output mode showing test names before execution
- Performance timing measurements
- Grouped test organization and display
- Test progress tracking
- File:
demo_with_failure.cpp
- Executable:
./bin/demo_with_failure
- Features demonstrated:
- Intentional test failures for error reporting demonstration
- Comprehensive error messages with file/line information
- Mixed passing and failing tests in the same runner
- Error handling and exit code behavior
- File:
demo_no_tests.cpp
- Executable:
./bin/demo_no_tests
- Features demonstrated:
- Behavior when no tests are registered or executed
- Default failure handling for empty test suites
- Use of
UTEST_ALLOW_EMPTY_TESTS()
for conditional testing
To run all demos:
mkdir _build && cd _build
cmake ..
cmake --build .
./run_demos.sh
To run individual demos:
./bin/utest_demo # Core features demo
./bin/demo_unicode_checkmarks # Unicode checkmarks demo
./bin/demo_verbose_mode # Verbose mode demo
./bin/demo_with_failure # Failure handling demo
./bin/demo_no_tests # No tests scenario demo
There are two ways of viewing test results:
- As standard application - from IDE or console, this will show which tests are failing, example:
OK: Test [assert_true] succeeded
OK: Test [assert_false] succeeded
OK: Test [assert_true_msg] succeeded
OK: Test [assert_false_msg] succeeded
Error: Test [assert_equals] failed!, error: Assertion failed, at /home/piotr/tmp/utest/tests/testUTest.cpp:33 in void test_assert_equals(): 42 != 43
OK: Test [assert_equals_msg] succeeded
OK: Test [assert_throws] succeeded
OK: Test [assert_throws_msg] succeeded
Failures!
- As
make test
orctest
- this will only show which runners are failing
piotr@piotr-Prec-M47:~/tmp/utest/_build$ make test
Running tests...
Test project /home/piotr/tmp/utest/_build
Start 1: testUTest,
1/1 Test #1: testUTest, .......................***Failed 0.00 sec
0% tests passed, 1 tests failed out of 1
Total Test time (real) = 0.00 sec
The following tests FAILED:
1 - testUTest, (Failed)
Errors while running CTest
make: *** [Makefile:84: test] Error 8
UTEST_PROLOG()
- Initialize the test frameworkUTEST_EPILOG()
- Finalize and report test resultsUTEST_FUNC(name)
- Run a test function (function should be namedtest_name
)UTEST_FUNC2(group, name)
- Run a grouped test function (function should be namedtest_group_name
)
UTEST_ALLOW_EMPTY_TESTS()
- Allow test runner to succeed even when no tests are runUTEST_USE_ASCII_CHECKMARKS()
- Use ASCII [OK]/[FAIL] instead of Unicode checkmarksUTEST_SHOW_PERFORMANCE()
- Enable performance timing for each testUTEST_ENABLE_VERBOSE_MODE()
- Show test names before execution (useful for debugging)
UTEST_FUNC_DEF(name)
- Define a test function:void test_name()
UTEST_FUNC_DEF2(group, name)
- Define a grouped test function:void test_group_name()
UTEST_ASSERT_TRUE(condition)
- Assert that condition is trueUTEST_ASSERT_TRUE_MSG(condition, msg)
- Assert that condition is true, with custom messageUTEST_ASSERT_FALSE(condition)
- Assert that condition is falseUTEST_ASSERT_FALSE_MSG(condition, msg)
- Assert that condition is false, with custom messageUTEST_ASSERT_EQUALS(a, b)
- Assert that a equals bUTEST_ASSERT_EQUALS_MSG(a, b, msg)
- Assert that a equals b, with custom messageUTEST_ASSERT_NOT_EQUALS(a, b)
- Assert that a does not equal bUTEST_ASSERT_NOT_EQUALS_MSG(a, b, msg)
- Assert that a does not equal b, with custom messageUTEST_ASSERT_STR_EQUALS(a, b)
- Assert that strings a and b are equalUTEST_ASSERT_STR_NOT_EQUALS(a, b)
- Assert that strings a and b are not equalUTEST_ASSERT_GT(a, b)
- Assert that a is greater than bUTEST_ASSERT_GTE(a, b)
- Assert that a is greater than or equal to bUTEST_ASSERT_LT(a, b)
- Assert that a is less than bUTEST_ASSERT_LTE(a, b)
- Assert that a is less than or equal to bUTEST_ASSERT_THROWS(F)
- Assert that function F throws an exceptionUTEST_ASSERT_THROWS_MSG(F, MSG)
- Assert that function F throws an exception, with custom messageUTEST_ASSERT_DOES_NOT_THROW(F)
- Assert that function F does not throw an exceptionUTEST_ASSERT_DOES_NOT_THROW_MSG(F, MSG)
- Assert that function F does not throw an exception, with custom messageUTEST_ASSERT_NULL(ptr)
- Assert that pointer ptr is nullUTEST_ASSERT_NOT_NULL(ptr)
- Assert that pointer ptr is not null
UTEST_ASSERT_EQ(a, b)
- Alias forUTEST_ASSERT_EQUALS
UTEST_ASSERT_NEQ(a, b)
- Alias forUTEST_ASSERT_NOT_EQUALS
UTEST_ASSERT_SEQ(a, b)
- Alias forUTEST_ASSERT_STR_EQUALS
UTEST_ASSERT_SNEQ(a, b)
- Alias forUTEST_ASSERT_STR_NOT_EQUALS
The build system organizes binaries into dedicated subdirectories:
- Main demo:
bin/utest_demo
- Additional demos:
bin/demo_unicode_checkmarks
,bin/demo_verbose_mode
,bin/demo_with_failure
,bin/demo_no_tests
- Tests:
bin/tests/test_*
By default, if no tests are run, the test framework will exit with failure. This can be changed using:
UTEST_PROLOG();
UTEST_ALLOW_EMPTY_TESTS(); // Allow success even with no tests
// No test functions called here
UTEST_EPILOG(); // Will return SUCCESS instead of FAILURE
This is useful for:
- Conditional test execution
- Test suites that may be empty under certain conditions
- Template-based test generation where some configurations may produce no tests
Tests can be organized into groups using UTEST_FUNC_DEF2
and UTEST_FUNC2
:
// Define grouped test functions
UTEST_FUNC_DEF2(ModuleA, Feature1) {
UTEST_ASSERT_TRUE(some_condition);
}
UTEST_FUNC_DEF2(ModuleA, Feature2) {
UTEST_ASSERT_EQUALS(calculate(), expected);
}
// Run grouped tests
UTEST_FUNC2(ModuleA, Feature1);
UTEST_FUNC2(ModuleA, Feature2);
In the test summary, tests with the same group name will be displayed together:
ModuleA:
[OK] Feature1
[OK] Feature2
Enable performance timing to see how long each test takes:
UTEST_PROLOG();
UTEST_SHOW_PERFORMANCE(); // Enable timing
UTEST_FUNC(my_test);
UTEST_EPILOG();
Output will show timing information:
[OK] Test [my_test] succeeded (1.234ms)
Enable verbose mode to see test names before execution:
UTEST_PROLOG();
UTEST_ENABLE_VERBOSE_MODE(); // Show test names before execution
UTEST_FUNC(my_test);
UTEST_EPILOG();
Output will show test names before execution:
Running test: my_test
[OK] Test [my_test] succeeded (1.234ms)
For grouped tests:
Running test: GroupName::TestName
[OK] Test [GroupName::TestName] succeeded (1.234ms)
For environments that don't support Unicode characters:
UTEST_PROLOG();
UTEST_USE_ASCII_CHECKMARKS(); // Use [OK]/[FAIL] instead of Unicode checkmarks
UTEST_FUNC(my_test);
UTEST_EPILOG();
The library now supports comprehensive string testing and modern C++ features:
// String testing with std::string and const char*
void test_strings() {
std::string str = "hello";
UTEST_ASSERT_EQUALS(str, "hello");
UTEST_ASSERT_EQUALS("world", "world");
}
// Lambda and functor testing
void test_lambdas() {
auto throwing_lambda = []() { throw std::exception(); };
UTEST_ASSERT_THROWS(throwing_lambda);
auto safe_lambda = []() { return 42; };
UTEST_ASSERT_DOES_NOT_THROW(safe_lambda);
}
The library takes advantage of several C++11 features to provide clean, modern syntax:
- Lambda expressions: For exception testing and complex test scenarios
- Auto keyword: For type deduction in test code
- Uniform initialization: For cleaner object construction
- std::chrono: For high-precision timing measurements
- nullptr: For safe null pointer testing
- static_assert: For compile-time type checking
- SFINAE with decltype: For template metaprogramming
Tested with:
- GCC 4.8+ (C++11 support)
- Clang 3.3+ (C++11 support)
- MSVC 2013+ (C++11 support)
- Any compiler with full C++11 support
See LICENSE.txt