AceUnit ManualAceUnit (Advanced C and Embedded Unit): a comfortable C code unit test framework. AceUnit is JUnit 4.x style, easy, modular and flexible. AceUnit can be used in resource constraint environments, e.g. embedded software development.
Unit Tests in AceUnit are declared using annotations. This works pretty much the same as JUnit 4.x. JUnit 4.x can be considered industry reference for unit test frameworks. Much of AceUnit was modelled after JUnit 4.x.
The most important annotation that you will need is A_Test.
The A_Test annotation tells AceUnit that a function is a unit test.
Simply place this as a modifier before your function declaration, where you would write static in case you would declare your function static.
This annotation will be the main interface between your unit tests and the AceUnit framework which will run them.
A_Test void someTestMethod() [
// test case implementation for this test case goes here
}Assume your source is named MyTest.c. There are only two more things which you will need in your source. Both of these things are very easy.
#include "MyTest.h" at the beginning of your source to include the required header file.java -jar AceUnit.jar MyTest >MyTest.h to generate this header file.That's all what you need.
AceUnit.jar?
AceUnit.jar is a Java program and the core for automation in AceUnit.
It scans a test source code and will generate all required information that the AceUnit framework will need to run your tests.
That information will be placed in the specified header file.
We know that running Java in a C environment is not convenient in
AceUnit supports the following styles of declaring unit tests:
A_Test void someTest() {...}When declaring tests with annotations, the following annotations are available:
A_TestA_BeforeA_AfterA_BeforeClassA_AfterClassA_IgnoreA_Test.
Writing AceUnit tests is easy, very easy.
fooOp() and fooData from foo.c.fooTest.c.Our scenario is developing a sorting algorithm. A sorting algorithm works if the list that it was sorting is sorted after the algorithm is done. To verify whether a sorting algorithm works, we need verify that its data was sorted. Both, the sorting and the verification need a comparator that determines whether an element should be sorted before or after another element.
The comparator works when it returns the expected result for each pair of values.
/** A Comparator compares two values and returns a comparison result. * @param o1 Pointer to first object to compare. * @param o2 Pointer to second object to compare. * @return Comparison result * @retval 0 if o1 and o2 are equal. * @retval <0 if o1 should be sorted before o2. * @retval >0 if o1 should be sorted after o2. */ typedef sint8_least_t(*comparator)(const void *o1, const void *o2);
For testing purposes, we want to sort ints. So the first thing that we do is we test whether our comparator implementation works.
/** Comparator for int values.
* @see comparator
*/
sint8_least_t compareInt(const void *o1, const void *o2) {
return 0;
}It is obvious that our current implementation is bogus. In test first programming, you intentionally start with a bogus implementation to verify that your test actually detects that the implementation is bogus.
/** Tests that {@link compareInt()} works. */
A_Test void testCompareInt() {
int n1;
int n2;
n1 = 0;
n2 = 0;
assertEquals("Comparing two equal numbers must return 0.", 0, compareInt(&0, &1));
n1 = 1;
n2 = 2;
assertTrue("Comparing 1 with 2 must return a value <0.", compareInt(&n1, &n2) < 0);
n1 = 2;
n2 = 1;
assertTrue("Comparing 2 with 1 must return a value >0.", compareInt(&n1, &n2) > 0);
}AceUnit supports the following options:
#define ACEUNIT_STATIC_ASSERTIONSstatic.
That way you have easy and flexible control over whether test methods are static or not.
Declaring test methods as static has a significant advantage.
You will be able to use the same name in different fixtures and still build and run them all together.
For logging, it is not required that test methods have unique names.
Logging will always also include the associated fixture (e.g. filename + line number).
So you will always uniquely identify which test case failed, even if the names of the test cases are not unique.
However, there are debuggers which will cause head ache if you try to debug functions which are static.
Some tool chains (compilers / linkers) will not emit proper debug information for functions which are static.
The pros and cons for static test methods are both very significant.
Whether or not you want to use static depends on your environment.
Because of that, we've left the choice to you.
But we've prepared everything for you.
Toggling between these two options is as simple as (not) defining this macro.
#define ACEUNIT_EMBEDDED