Utest.h
🧪 single header unit testing framework for C and C++
Install / Use
/learn @sheredom/Utest.hREADME
🧪 utest.h
A simple one header solution to unit testing for C/C++.
Usage
Just #include "utest.h" in your code!
The current supported platforms are Linux, macOS and Windows.
The current supported compilers are gcc, clang, MSVC's cl.exe, and clang-cl.exe.
It also works with tcc but with a caveat: the latest release of the tcc compiler (version 0.9.27) lacks a feature for UTEST to work. Make sure to use a tcc that is patched with the constructor attribute extension. Recent Ubuntu and Debian Linux distros ship tcc with that patch already included. If you compile tcc yourself, use the trunk version and it will work as expected.
Command Line Options
utest.h supports some command line options:
--helpto output the help message--filter=<filter>will filter the test cases to run (useful for re-running one particular offending test case).--list-testswill list testnames, one per line. Output names can be passed to--filter.--output=<output>will output an xunit XML file with the test results (that Jenkins, travis-ci, and appveyor can parse for the test results).--enable-mixed-unitswill enable the per-test output to contain mixed units (s/ms/us/ns).--random-order[=<seed>]will randomize the order that the tests are ran in. If the optional <seed> argument is not provided, then a random starting seed is used.
Design
UTest is a single header library to enable all the fun of unit testing in C and C++. The library has been designed to provide an output similar to Google's googletest framework:
[==========] Running 1 test cases.
[ RUN ] foo.bar
[ OK ] foo.bar (631ns)
[==========] 1 test cases ran.
[ PASSED ] 1 tests.
UTEST_MAIN
In one C or C++ file, you must call the macro UTEST_MAIN:
UTEST_MAIN()
This will call into utest.h, instantiate all the testcases and run the unit test framework.
Alternatively, if you want to write your own main and call into utest.h, you can instead, in one C or C++ file call:
UTEST_STATE();
And then when you are ready to call into the utest.h framework do:
int main(int argc, const char *const argv[]) {
// do your own thing
return utest_main(argc, argv);
}
Define a Testcase
To define a test case to run, you can do the following;
#include "utest.h"
UTEST(foo, bar) {
ASSERT_TRUE(1);
}
The UTEST macro takes two parameters - the first being the set that the test case belongs to, the second being the name of the test. This allows tests to be grouped for convenience.
Define a Fixtured Testcase
A fixtured testcase is one in which there is a struct that is instantiated that can be shared across multiple testcases.
struct MyTestFixture {
char c;
int i;
float f;
};
UTEST_F_SETUP(MyTestFixture) {
utest_fixture->c = 'a';
utest_fixture->i = 42;
utest_fixture->f = 3.14f;
// we can even assert and expect in setup!
ASSERT_EQ(42, utest_fixture->i);
EXPECT_TRUE(true);
}
UTEST_F_TEARDOWN(MyTestFixture) {
// and also assert and expect in teardown!
ASSERT_EQ(13, utest_fixture->i);
}
UTEST_F(MyTestFixture, a) {
utest_fixture->i = 13;
// teardown will succeed because i is 13...
}
UTEST_F(MyTestFixture, b) {
utest_fixture->i = 83;
// teardown will fail because i is not 13!
}
Some things to note that were demonstrated above:
- We have this new implicit variable within our macros - utest_fixture. This is a pointer to the struct you decided as your fixture (so MyTestFixture in the above code).
- Instead of specifying a testcase set (like we do with the UTEST macro), we instead specify the name of the fixture struct we are using.
- Every fixture has to have a
UTEST_F_SETUPandUTEST_F_TEARDOWNmacro - even if they do nothing in the body. - Multiple testcases (UTEST_F's) can use the same fixture.
- You can use EXPECT_* and ASSERT_* macros within the body of both the fixture's setup and teardown macros.
Define an Indexed Testcase
Sometimes you want to use the same fixture and testcase repeatedly, but perhaps subtly change one variable within. This is where indexed testcases come in.
struct MyTestIndexedFixture{
bool x;
bool y;
};
UTEST_I_SETUP(MyTestIndexedFixture) {
if (utest_index < 30) {
utest_fixture->x = utest_index & 1;
utest_fixture->y = (utest_index + 1) & 1;
}
}
UTEST_I_TEARDOWN(MyTestIndexedFixture) {
EXPECT_LE(0, utest_index);
}
UTEST_I(MyTestIndexedFixture, a, 2) {
ASSERT_TRUE(utest_fixture->x | utest_fixture->y);
}
UTEST_I(MyTestIndexedFixture, b, 42) {
// this will fail when the index is >= 30
ASSERT_TRUE(utest_fixture->x | utest_fixture->y);
}
Note:
- We use UTEST_I_* as the prefix for the setup and teardown functions now.
- We use UTEST_I to declare the testcases.
- We have access to a new variable utest_index in our setup and teardown functions, that we can use to slightly vary our fixture.
- We provide a number as the third parameter of the UTEST_I macro - this is the number of times we should run the test case for that index. It must be a literal.
Testing Macros
Matching what googletest has, we provide two variants of each of the error checking conditions - ASSERTs and EXPECTs. If an ASSERT fails, the test case will cease execution, and utest.h will continue with the next test case to be run. If an EXPECT fails, the remainder of the test case will still be executed, allowing for further checks to be carried out.
We currently provide the following macros to be used within UTESTs:
ASSERT_TRUE(x)
Asserts that x evaluates to true (EG. non-zero).
UTEST(foo, bar) {
int i = 1;
ASSERT_TRUE(i); // pass!
ASSERT_TRUE(42); // pass!
ASSERT_TRUE(0); // fail!
}
ASSERT_FALSE(x)
Asserts that x evaluates to false (EG. zero).
UTEST(foo, bar) {
int i = 0;
ASSERT_FALSE(i); // pass!
ASSERT_FALSE(1); // fail!
}
ASSERT_EQ(x, y)
Asserts that x and y are equal.
UTEST(foo, bar) {
int a = 42;
int b = 42;
ASSERT_EQ(a, b); // pass!
ASSERT_EQ(a, 42); // pass!
ASSERT_EQ(42, b); // pass!
ASSERT_EQ(42, 42); // pass!
ASSERT_EQ(a, b + 1); // fail!
}
ASSERT_NE(x, y)
Asserts that x and y are not equal.
UTEST(foo, bar) {
int a = 42;
int b = 13;
ASSERT_NE(a, b); // pass!
ASSERT_NE(a, 27); // pass!
ASSERT_NE(69, b); // pass!
ASSERT_NE(42, 13); // pass!
ASSERT_NE(a, 42); // fail!
}
ASSERT_LT(x, y)
Asserts that x is less than y.
UTEST(foo, bar) {
int a = 13;
int b = 42;
ASSERT_LT(a, b); // pass!
ASSERT_LT(a, 27); // pass!
ASSERT_LT(27, b); // pass!
ASSERT_LT(13, 42); // pass!
ASSERT_LT(b, a); // fail!
}
ASSERT_LE(x, y)
Asserts that x is less than or equal to y.
UTEST(foo, bar) {
int a = 13;
int b = 42;
ASSERT_LE(a, b); // pass!
ASSERT_LE(a, 27); // pass!
ASSERT_LE(a, 13); // pass!
ASSERT_LE(27, b); // pass!
ASSERT_LE(42, b); // pass!
ASSERT_LE(13, 13); // pass!
ASSERT_LE(13, 42); // pass!
ASSERT_LE(b, a); // fail!
}
ASSERT_GT(x, y)
Asserts that x is greater than y.
UTEST(foo, bar) {
int a = 42;
int b = 13;
ASSERT_GT(a, b); // pass!
ASSERT_GT(a, 27); // pass!
ASSERT_GT(27, b); // pass!
ASSERT_GT(42, 13); // pass!
ASSERT_GT(b, a); // fail!
}
ASSERT_GE(x, y)
Asserts that x is greater than or equal to y.
UTEST(foo, bar) {
int a = 42;
int b = 13;
ASSERT_GE(a, b); // pass!
ASSERT_GE(a, 27); // pass!
ASSERT_GE(a, 13); // pass!
ASSERT_GE(27, b); // pass!
ASSERT_GE(42, b); // pass!
ASSERT_GE(13, 13); // pass!
ASSERT_GE(42, 13); // pass!
ASSERT_GE(b, a); // fail!
}
ASSERT_MEMEQ(x, y, len)
Asserts that the memory region x and y are equal.
UTEST(foo, bar) {
const char foo[4] = {1, 2, 3, 5};
const char bar[4] = {1, 2, 3, 4};
ASSERT_MEMEQ(foo, foo, 4); // pass!
ASSERT_MEMEQ(bar, bar, 4); // pass!
ASSERT_MEMEQ(foo, bar, 4); // fail!
}
ASSERT_STREQ(x, y)
Asserts that the strings x and y are equal.
UTEST(foo, bar) {
char* a = "foo";
char* b = "bar";
ASSERT_STREQ(a, a); // pass!
ASSERT_STREQ(b, b); // pass!
ASSERT_STREQ(a, b); // fail!
}
ASSERT_STRNE(x, y)
Asserts that the strings x and y are not equal.
UTEST(foo, bar) {
char* a = "foo";
char* b = "bar";
ASSERT_STRNE(a, b); // pass!
ASSERT_STRNE(a, a); // fail!
}
ASSERT_STRNEQ(x, y)
Asserts that the strings x and y are equal up to the length of the string x.
UTEST(foo, bar) {
char* a = "foobar";
char* b = "foo";
ASSERT_STRNEQ(a, a); // pass!
ASSERT_STRNEQ(b, b); // pass!
ASSERT_STRNEQ(a, b); // pass!
}
ASSERT_STRNNE(x, y)
Asserts that the strings x and y are not equal up to the length of the string x.
UTEST(foo, bar) {
char* a = "foobar";
char* b = "bar";
ASSERT_STRNNE(a, b); // pass!
ASSERT_STRNNE(a, a); // fail!
}
ASSERT_NEAR(x, y, epsilon)
Asserts that the floating-point values x and y are within epsilon distance of each other.
UTEST(foo, bar) {
float a = 42.0f;
float b = 42.01f;
ASSERT_NEAR(a, b, 0.01f); // pass!
ASSERT_NEAR(a, b, 0.001f); // fail!
}
ASSERT_EXCEPTION(x, exception_type)
Asserts that exception_type will be thrown when code x is executed.
void foo(int bar) {
if (bar == 1)
throw std::range_error;
}
UTEST(foo, bar) {
ASSERT_EXCEPTION(foo(1), std::range_error); // pass!
ASSERT_EXCEPTION(foo(2), std::range_error); // fail!
ASSERT_EXCEPTION(foo(1), std::exception); // fail!
}
EXPECT_TRUE(x)
Expects that x evaluat
Related Skills
gh-issues
347.0kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
107.8kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
