Testing C Code¶
mu::tiny supports writing tests in pure C. Because the test runner itself is C++, you need two files per test group: a C file containing the tests, and a C++ wrapper that registers them with the C++ runner.
The Two-File Pattern¶
The C test file¶
Include “mu/tiny/test.h” and use the C macros:
#include "hello.h"
#include "mu/tiny/test.h"
static const char* output_ptr;
static int (*saved_print)(const char*, ...);
static int capture_output(const char* format, ...)
{
output_ptr = format;
return 1;
}
TEST_GROUP_SETUP(hello)
{
saved_print = print_formated;
print_formated = capture_output;
output_ptr = NULL;
}
TEST_GROUP_TEARDOWN(hello)
{
print_formated = saved_print;
}
TEST(hello, PrintsHelloWorld)
{
print_hello_world();
CHECK_EQUAL_STRING("Hello World!\n", output_ptr);
}
TEST_GROUP_SETUP and TEST_GROUP_TEARDOWN in C define wrapper
functions that the C++ file calls. TEST in C defines a
wrapper function body.
The C++ registration wrapper¶
#include "mu/tiny/test.hpp"
TEST_GROUP_C_WRAPPER(hello)
{
TEST_GROUP_C_SETUP_WRAPPER(hello)
TEST_GROUP_C_TEARDOWN_WRAPPER(hello)
};
TEST_C_WRAPPER(hello, PrintsHelloWorld)
Both files must be compiled together into the same test executable:
add_executable(my_tests main.cpp foo_test.c foo_test.cpp)
target_link_libraries(my_tests PRIVATE mu::tiny)
C Assertion Macros (mu/tiny/test.h)¶
The C header provides typed macros because C does not have overloaded functions:
Macro |
Type checked |
|---|---|
any non-zero value |
|
same with message |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
raw memory via |
|
unconditional failure with message |
|
attaches metadata to JUnit output |
Each macro also has a _TEXT variant that accepts an extra message
string.
C Test Definition Macros¶
Macro |
Purpose |
|---|---|
Defines the setup function for group |
|
Defines the teardown function for group |
|
Defines a test function body |
|
Defines an skipped test body |
|
Defines an expected-failure test body |
C++ Wrapper Macros¶
Each C macro that defines a test body has a matching C++ wrapper:
C++ wrapper |
What it creates |
|---|---|
A |
|
Calls the C setup function from C++ |
|
Calls the C teardown function from C++ |
|
A |
|
An |
|
An |
C Mock Interface (mu/tiny/mock.h)¶
-
struct MutinyMockSupport¶
[source] Vtable of function pointers that form the C mock API. Obtain a pointer via
mutiny_mock()ormutiny_mock_scope().
-
MutinyMockSupport *mutiny_mock(void)¶
[source] Returns the global
MutinyMockSupportinstance.
-
MutinyMockSupport *mutiny_mock_scope(const char *name)¶
[source] Returns the named
MutinyMockSupportscope.
The C mock API exposes MutinyMockSupport through
mutiny_mock(), which returns a pointer to a struct of function
pointers. This allows chaining calls in C99:
#include "mu/tiny/mock.h"
/* expect */
mutiny_mock()->expect_one_call("send")
->with_int_parameters("fd", 3)
->and_return_int_value(8);
/* actual (in stub) */
int n = (int)mutiny_mock()
->actual_call("send")
->with_int_parameters("fd", fd)
->int_return_value();
For named scopes: mutiny_mock_scope("name") returns a pointer to
the named MutinyMockSupport.
Data store in C¶
mutiny_mock()->set_int_data("retval", 42);
int v = mutiny_mock()->get_data("retval").value.int_value;
Custom comparators in C¶
static int my_equal(const void* a, const void* b) { return a == b; }
static const char* my_to_string(const void* a) { (void)a; return "obj"; }
mutiny_mock()->install_comparator("MyType", my_equal, my_to_string);
Complete C mock example:
#include "mu/tiny/mock.h"
#include "mu/tiny/test.h"
static int equal_method(const void* object1, const void* object2)
{
return object1 == object2;
}
static const char* to_string_method(const void* object)
{
(void)object;
return "string";
}
TEST(MockDocumentation_C, CInterface)
{
void* object = (void*)0x1;
mutiny_mock()
->expect_one_call("foo")
->with_int_parameters("integer", 10)
->and_return_double_value(1.11);
double d = mutiny_mock()
->actual_call("foo")
->with_int_parameters("integer", 10)
->return_value()
.value.double_value;
CHECK_EQUAL_DOUBLE(1.11, d, 0.00001);
mutiny_mock()->install_comparator("type", equal_method, to_string_method);
mutiny_mock_scope("scope")->expect_one_call("bar")->with_parameter_of_type(
"type", "name", object
);
mutiny_mock_scope("scope")->actual_call("bar")->with_parameter_of_type(
"type", "name", object
);
mutiny_mock()->remove_all_comparators_and_copiers();
mutiny_mock()->set_int_data("important", 10);
mutiny_mock()->check_expectations();
mutiny_mock()->clear();
}
Examples¶
Files |
Demonstrates |
|---|---|
Two-file pattern: stubs a function pointer with |
|
C mock interface: |