Skip to content
Benchmarks

Benchmarks

Methodology

The reproducible benchmark generates equivalent 5,000-test suites for each framework. C frameworks are compiled as C11 with clang -O2; C++ frameworks are compiled as C++11 with clang++ -O2. Keeping the languages in separate tables avoids treating results from different compilers and language runtimes as directly comparable.

Each suite is measured in three runtime modes: one filtered passing test, all passing tests, and all failing tests. The report also records preprocessed line count, compile time, object size, and executable size. Runtime output is suppressed while measuring. Compile time uses one warmup and the median of three measured runs; runtime uses two warmups and the median of ten measured runs.

The compile metric covers the generated test translation unit. Unity and GoogleTest normally use a separately compiled support library, so that one-time library build is excluded from compile time. Its object bytes are still included in the object-size total, and its linked code is included in the executable size. Header-only frameworks compile their runner as part of the measured translation unit. All runtime measurements execute tests in-process; Acutest is invoked with --no-exec to disable its default per-test child-process isolation.

Minimal C example
#define JC_TEST_IMPLEMENTATION
#include <jc_test.h>

TEST(Math, Multiply)
{
    ASSERT_EQ(4, 2 * 2);
}

int main(int argc, char** argv)
{
    jc_test_init(&argc, argv);
    return jc_test_run_all();
}
Minimal C++ example
#define JC_TEST_IMPLEMENTATION
#include <jc_test.hpp>

TEST(MyTest, Multiplication) {
    ASSERT_EQ(4, 2 * 2);
}
TEST(MyTest, Division) {
    ASSERT_EQ(2, 4 / 2);
}

int main(int argc, char *argv[]) {
    // Parses and removes any jc_test specific arguments
    jc_test_init(&argc, argv);
    // ... Do your test initialization
    return jc_test_run_all();
}

The all-fail jctest cases retain and print every failure again in the final recap. They therefore do additional allocation and formatting work that the comparison frameworks may not perform. Results are informational rather than timing gates in CI.

Reproduce the benchmark

python3 test/comparisons/benchmark.py \
    --utest-dir /path/to/utest.h-checkout \
    --greatest-dir /path/to/greatest-checkout \
    --unity-dir /path/to/Unity-checkout \
    --acutest-dir /path/to/acutest-checkout \
    --gtest-dir /path/to/googletest-checkout \
    --doctest-dir /path/to/doctest-checkout \
    --catch2-dir /path/to/Catch2-v2-checkout \
    --output build/comparisons/results.md

The report records the platform, compiler versions, workload, and exact dependency commits. The generated sources, object files, and executables are kept under build/comparisons for inspection.

Reference run: 2026-07-23

Apple Clang 21.0.0 on arm64 macOS 27.0. GoogleTest 1.12.1 and Catch2 2.13.10 are the last release lines of those frameworks that support C++11. Exact revisions for every dependency are recorded by the generated report.

External framework revisions
  • utest.h: ebb62ed381c4f32ef2ddafb56890bcae1472ad9d
  • greatest: 11a6af1919049df502405913da64fb385c219361
  • Unity: 3a6eb6dfd7706b703adf60f5ce3bcad57f94de4f
  • Acutest: 31751b4089c93b46a9fd8a8183a695f772de66de
  • GoogleTest 1.12.1: 58d77fa8070e8cec2dc1ed015d66b454c8d78850
  • doctest 2.4.12: 1da23a3e8119ec5cce4f9388e91b065e20bf06f5
  • Catch2 2.13.10: 182c910b4b63ff587a3440e08f84f70497e49a81

C frameworks

FrameworkPreprocessed linesCompileObjectExecutableSingle filterAll passAll fail
jc_test C8,67511.053856s1,729,648988,6080.002821s0.003776s0.009019s
utest.h118,31514.368594s4,441,4722,239,1120.003942s0.003859s0.005930s
greatest12,4991.863712s607,464317,1360.003098s0.009336s0.010399s
Unity11,6345.135419s1,859,872749,8160.003487s0.007473s0.010356s
Acutest15,1720.674482s1,473,560736,1440.003074s0.008260s0.010067s

C++ frameworks

FrameworkPreprocessed linesCompileObjectExecutableSingle filterAll passAll fail
jc_test C++12,5083.688477s4,705,7203,272,7040.014315s0.022721s0.029285s
GoogleTest75,20025.334601s11,501,5768,639,8320.005366s0.013761s0.025260s
doctest174,85326.525474s9,808,8886,117,2640.004898s0.013832s0.027865s
Catch2 v2204,29312.751911s6,255,3364,120,6320.005428s0.013625s0.049609s

Real life examples

In our case (the Defold game engine), we wanted to replace GTEST with something new.

Here’s a comparison between the GTEST and jctest frameworks when building the game engine tests. We see a decrease of the compile time, ending up at ~54% of the original time.

Compile times
Compile times

The footprint on disc is also important, as it takes up both space but also affects writing/reading times. In this scenario the file sizes ended up at ~45% of the original sizes.

Unit test sizes
Unit test sizes
Last updated on