Audition: The C Testing Framework Ready for Its Big Break
Posted on 2024-11-11T15:51:00Z #audition
Today, Railgun Labs proudly announces Audition a modern xUnit testing framework for C11 and beyond.
Audition is an all-in-one testing framework for C. It’s built with the features one expects from an xUnit testing framework — and so much more. In this announcement post, we’ll explore some of its unique features.
Automatic Test Registration
Traditional C testing frameworks require boilerplate code to register tests. This means writing the test and then registering it with a test suite. If you don’t have a test suite created, it becomes an additional step.
With Audition, this process becomes a one-step wonder. Just write your test, and Audition handles the registration.
The following code defines a complete executable program with a single test case.
In this program, the test is named myTest
and the suite it belongs to is named mySuite
.
#include <audition.h>
TEST(mySuite, myTest) {
ASSERT_EQ(1 + 2, 3);
}
Notice the absence of a main
function?
Audition provides its own, so you can focus on what matters: your tests.
By default, Audition will execute all tests when your unit test program is executed. If you want to run a specific test, you can do so by specifying its suite and test name (delimited by a period character) at the command line.
$ ./your_test_program.exe mySuite.myTest
1 passing [100%] (1ms)
Glob patterns are also supported so a pattern like, mySuite.my*
would instruct Audition to run every test that begins with my
belonging to the suite mySuite
.
Function Mocking
Contemporary C unit testing frameworks lack native function mocking. Those that do are limited and usually involve non-portable linker flags or code generators. These approaches also require access to the source code (or at least the object files) which is something you may or may not have.
Audition is different. With Audition you can trivially mock C functions, including functions you lack the source code too!
Audition offers two types of mocks: fakes and stubs.
Fakes
Fake functions are a special function that all calls to the original function are redirected to.
The fake function is a function you implement.
In it you can assert the arguments and return a hard-coded return value.
In the following code snippet all calls to the foo
function are redirected to the mock_foo
function.
#include <audition.h>
int foo(int); // original function, forward declared
static int fake_foo(int x) { // fake function
EXPECT_GTEQ(x, 0); // assert argument x >= 0
return 42; // return hard-coded value
}
TEST(mySuite, myTest) {
FAKE(foo, fake_foo); // redirect foo() to fake_foo()
foo() // returns 42
}
Stubs
For scenarios where you only need to return a hard-coded value, Audition provides a shorthand called function stubbing. When you stub a function you provide the return value and let Audition handle the rest.
#include <audition.h>
int foo(int); // original function, forward declared
TEST(mySuite, myTest) {
STUB(foo, 42); // make foo() to always return 42
foo(); // returns 42
}
Reporting Formats
Audition’s default output format is designed for terminal display, but it also supports TAP, JUnit, and Subunit formats. Specifically, Audition supports TAP format version 12, 13, and 14; JUnit XML version 5 and; subunit version 1. You can choose the output format via command line arguments.
Here’s an example of TAP v13 output:
$ ./your_test_program.exe --reporter=tap
TAP version 13
1..3
ok 1 - spaceship.closeDoors
ok 2 - spaceship.checkFuel
ok 3 - spaceship.blastOff
And here’s JUnit XML output:
$ ./your_test_program.exe --reporter=junit
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites tests="3" failures="0" time="0.0">
<testsuite name="spaceship" tests="3" failures="0">
<testcase name="closeDoors" />
<testcase name="checkFuel" />
<testcase name="blastOff" />
</testsuite>
</testsuites>
JUnit XML is also a favorite among CI tools, making Audition integration with dashboards a breeze.
Other Goodies
Audition includes many other features and quality of life improvements.
Generic Assertion Macros
Audition uses C11’s _Generic
to provide type-generic assertion macros.
This allows you to use the same assertion macro for different types.
The snippet below shows the ASSERT_EQ
assertion being used to test equality between integers and floating-pointer numbers.
ASSERT_EQ(1, 2); // compare integers
ASSERT_EQ(1.0, 2.0); // comparing floating-point numbers
Binary and Text Diffing
Audition supports multi-line string diffing for easy comparison of multi-line strings. The excerpt below demonstrates diff output from Audition after comparing two mismatching multi-line strings. One string is from Edger Allen Poe’s The Raven and the other is a knockoff. I’ll let you guess which is which.
@@ -35,7 +35,7 @@
Open here I flung the shutter, when, with many a flirt and flutter,
-In there stepped a stately Raven of the saintly days of yore.
+In there stepped a stately Crow of the saintly days of yore.
Not the least obeisance made he; not an instant stopped or stayed he;
Audition also supports binary diffs.
@@ -0000,41 +0000,38 @@
-0000 | 41 | A |
0001 0000 | DE AD BE EF BA AD F0 0D BA 6D | .........m |
-000E | 00 | . |
+ 000D | BE AF C0 | ... |
Platform Abstractions
Audition implements various cross-platform utilties presented through a cross platform interface. Among these include portable file system utilities (e.g. create directory, iterate files in directory) and high-resolution timers.
Closing Thoughts
Audition brings a unique combination of features unmatched by other C testing frameworks. With many more quality-of-life improvements not covered here! Audition will transform your testing experience.
Audition is available for Windows, macOS, and Ubuntu Linux. Download it for your platform here..