A minimalist C unit testing framework
- A single header-file with plain C and
#define
macros. - Plays well with CTest with a
discover_tests()
CMake function.
mtest is a simple, C-only unit testing framework inspired by CuTest with some modern features inspired by doctest.
For more mature and feature-complete C/C++ testing frameworks, take a look at Boost.Test, Catch2, doctest, or Google Test.
Table of Contents
Ensure the CMake library target mtest
is available, see the below Installation section.
Once you have installed mtest
, add something like the following code to your CMakeLists.txt.
# Put `FetchContent_MakeAvailable(mtest)`, `add_subdirectory(mtest)` or `find_package(mtest)` here.
add_executable(my_example_test my_test.c)
target_link_libraries(my_example_test mtest)
# Automatically discover test cases in my_example_test and add them to CTest.
discover_tests(my_example_test)
Where the example test file my_test.c
looks something like the following:
#include "mtest.h"
TEST_CASE(my_first_test_case, {
int i = 42;
CHECK_EQ_INT(42, i); // A simple check, similar to asking if `42 == i`.
})
TEST_CASE(my_other_test_case, {
double i = 3.14;
CHECK_EQ_DOUBLE(3.14, i, 0.01); // When comparing doubles, we need to specify a tolerance - here we choose 0.01
})
TEST_CASE(my_dependent_test_case, {
int i = 0;
// REQUIRE checks will stop executing a test case early if it fails.
// This is helpful for when our test modifies a shared variable. For example:
REQUIRE_EQ_INT(0, i++);
// Now the following check would only make sense if the above condition succeeded.
// Therefore, we say the above test is required, since otherwise our check wouldn't
// give us any meaningful output.
CHECK_EQ_INT(1, i);
})
// This macro creates a main function that can call each test case, and it tells CTest which test cases are available.
MAIN_RUN_TESTS(my_first_test_case, my_other_test_case, my_dependent_test_case)
See mtest.h
for all test macros.
Recommended installation using CMake's FetchContent to download the latest release:
include(FetchContent)
FetchContent_Declare(mtest
URL https://github.com/MortenSchou/mtest/archive/refs/tags/v0.2.0.zip
URL_HASH SHA256=bec1e90fb00a5bc155de12ed5ed39ea5d1a1b6fcfb6c80cce5ad3e38e360248c
)
FetchContent_MakeAvailable(mtest)
Or alternatively you can specify the Git repository and version to download:
include(FetchContent)
FetchContent_Declare(mtest
GIT_REPOSITORY https://github.com/MortenSchou/mtest.git
GIT_TAG main # Or specify a specific branch, tag, or commit hash.
)
FetchContent_MakeAvailable(mtest)
Updates can be done by changing the URL
and URL_HASH
in the first approach,
or by changing the GIT_TAG
in the second approach.
Lets you manage mtest as a linked directory managed by git. This is the more traditional way of linking dependencies.
Open a terminal and ensure you are inside your project's root (where your main CMakeLists.txt file is located). Then type the following:
mkdir extern
git submodule add https://github.com/MortenSchou/mtest.git extern/mtest
git commit -m "add mtest dependency"
Now you can add the following to your CMakeLists.txt:
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/mtest/CMakeLists.txt")
message(FATAL_ERROR "The git submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Did you forget to run `git submodule init` after cloning?")
else()
add_subdirectory(extern/mtest)
endif()
Updating mtest can be done with the git submodule update
command, then
using git to commit the updated submodule directory.
This is likely not what you want, and it is recommended you pick one of the other approaches, as this does not ensure that anyone using your project will have the same mtest library installed as you, or even that its installed.
However, this approach can be helpful if you are packaging your project, or have otherwise installed mtest as part of your operating system.
If you do not have mtest already installed, you can run the following two commands:
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
sudo cmake --install build
Then you can add the following to your primary CMakeLists.txt:
find_package(mtest REQUIRED)
To update when using cmake --install
, you need to git pull
this project,
and rerun the above cmake commands.
To uninstall the cmake --install
approach, you need to go manually
delete each file as listed in the generated cmake_build/install_manifest.txt
file.