(Top)
0.1 What is µTest?
0.2 Adding µTest to your project
0.3 Writing tests using µTest
0.4 Output formats
0.5 API Reference
0.6 Copyright and license
µTest aims to be a small unit testing library for C projects, with an API heavily modelled on high level Behavior-Driven Development frameworks like Jasmine or Mocha.
The preferred way to use µTest is to include it in your own projects using
a Git sub-module, and then building µTest as a static library. If you are
using Meson, you can use a wrap file in the
subprojects
directory:
[wrap-git]
directory=mutest
url=https://github.com/ebassi/mutest.git
revision=master
and then use the fallback
dependency type:
mutest_dep = dependency('mutest-1',
fallback: [ 'mutest', 'mutest_dep' ],
default_options: ['static=true'],
required: false,
disabler: true,
)
static
option will build µTest as an uninstalled static library.
You can remove the default_options
argument to get an installed
shared library.
µTest can also build a shared library and a pkg-config
file to describe the necessary compiler and linker flags, if you want to
install µTest in your library path; if you decide to depend on the shared
library, you can use the normal pkg-config
-based dependency discovery.
For instance, if you are using Meson:
mutest_dep = dependency('mutest-1')
And if you're using Autotools:
PKG_CHECK_MODULES(MUTEST, [mutest-1])
AC_SUBST(MUTEST_CFLAGS)
AC_SUBST(MUTEST_LIBS)
Writing a test program using µTest requires initializing the test framework,
defining the various tests, and gathering the results. For convenience, µTest
provides a simple macro meant to replace the main
entry point of any C
binary:
MUTEST_MAIN (
// your code goes here
)
The MUTEST_MAIN
macro will initialize µTest and print the report.
Each µTest-based test starts from a suite, which is created by calling
mutest_describe()
in the main entry point of your test binary:
MUTEST_MAIN (
mutest_describe ("A test suite", main_suite);
)
A test binary can have multiple suites.
The purpose of the main_suite()
function is to create a specification,
or spec. A spec describes an aspect of your data type, service, or API.
Each spec is made of various expectations, which are defined by calling
mutest_it()
:
static void
main_suite (void)
{
mutest_it ("is made of at least one spec", first_spec);
mutest_it ("can contain multiple specs", second_spec);
mutest_it ("can have specs that can be skipped", skipped_spec);
}
As you can see, the description of suites and specs tries to match a natural language description of what the suite is about, and what aspect of the API is covered by each spec. The descriptions will be logged in the test output.
Inside each spec we have expectations that must be satisfied, in order to say that the test suite is successful. An expectation is made of three parts:
static void
first_spec (void)
{
bool a = true;
mutest_expect ("a to be true", mutest_bool_value (a),
mutest_to_be, true, NULL);
mutest_expect ("a not to be false", mutest_bool_value (a),
mutest_not, mutest_to_be, false,
NULL);
}
Expectations can chain multiple matching functions in order to create multiple conditions that must be all satisfied:
static void
second_spec (void)
{
// The get_greeting() function is defined elsewhere, and
// we are testing its output
const char *str = get_greeting ();
mutest_expect ("get_greeting() to return all parts of a greeting",
mutest_string_value (str),
mutest_to_start_with_string, "hello",
mutest_to_contain, ",",
mutest_to_end_with_string, "world",
NULL);
}
In the example above, the expectation will only be satisfied if all three matchers are successful.
Expectations can be programmatically skipped. For instance, if you haven't implemented some functionality, yet. These expectations will be satisfied, but still logged not as successes:
static void
skipped_spec (void)
{
// The get_answer() function is defined elsewhere
int answer = get_answer ();
mutest_expect ("an answer to the ultimate question of life, the universe, and everything",
mutest_int_value (answer),
mutest_skip, "Deep Thought hasn't finished yet",
NULL);
}
In the example above, the skipped_spec()
specification will succeed, but
it will be marked as skipped.
By default, µTest uses an output similar to Mocha:
$ ./test-suite
A test suite
is made of at least one spec
✓ a to be true
✓ a not to be false
2 passing (80.0 µs)
can contain multiple specs
✓ get_greeting() to return all parts of a greeting
1 passing (60.0 µs)
can have specs that can be skipped
- an answer to the ultimate question of life, the universe and everything
0 passing (34.0 µs)
1 skipped
Total
3 passing (202.0 µs)
1 skipped
Additionally, µTest supports TAP—Test Anything
Protocol. If you have a TAP harness, like prove(1),
you can use the MUTEST_OUTPUT
environment variable set to tap
in order to
get a TAP output:
$ MUTEST_OUTPUT=tap ./test-suite
# A test suite
# is made of at least one spec
ok 1 - a to be true
ok 2 - a not to be false
# can contain multiple specs
ok 3 - get_greeting() to return all parts of a greeting
# can have specs that can be skipped
ok 4 - an answer to the ultimate question of life, the universe and everything # SKIP: Deep Thought hasn't finished yet
1..4
Copyright 2019 Emmanuele Bassi
µTest and this documentation are released under the terms of the MIT license.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.