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
autokeyword for type deduction- Uniform initialization syntax (
{}) std::enable_iffor SFINAEstd::chronofor timingnullptrandstd::nullptr_tstatic_assertfor compile-time checksdecltypefor 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 testmkdir _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--detailedfor 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.shUTEST_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.cppThe 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_DEF2syntax - 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.shTo 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 demoThere 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 testorctest- 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_EQUALSUTEST_ASSERT_NEQ(a, b)- Alias forUTEST_ASSERT_NOT_EQUALSUTEST_ASSERT_SEQ(a, b)- Alias forUTEST_ASSERT_STR_EQUALSUTEST_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 FAILUREThis 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