UNITY

Unit Testing for C (especially Embedded Software)

C

Unity is written in 100% pure C code. It follows ANSI standards while supporting most embedded compiler quirks.

Portable

Unity is equally happy running tests for an 8-bit microcontroller as it is a 64-bit processor on steroids.

Expressive

Unity is designed to help you make the most of your test suite. It features a rich set of assertions so you can find the perfect match for your needs

QUICK

Unity is pure C. Compile. Run. Results.
Run as native executables on your host system or in a simulator for your target!

Simple

Unity is easily integrated into your toolchain. Start with just the basics, then power up with optional scripts or features later.

Extensible

Unity is easily expanded. Build new macros to fit your own custom types. Pull in CMock for full mocking support. Script the process or the results. Endless Possibilities!

Chalkboard_by_DallasKrentzel.jpg

HOW UNITY WORKS

Unity is most obviously about assertions. Assertions are statements of what we expect to be true about our embedded system. At their most basic, they are things like this:

int a = 1;
TEST_ASSERT( a == 1 ); //this one will pass
TEST_ASSERT( a == 2 ); //this one will fail

You could use nothing but the TEST_ASSERT above, and you could test almost anything that your C code can handle... but when something went wrong, you'd see something like this:

TestMyModule.c:15:test_One:FAIL

While correct, it's not terribly informative. There are two ways to fix this. The brute force method (good for your non-standard corner cases):

TEST_ASSERT_MESSAGE( a == 2 , "a isn't 2, end of the world!");

which results in:

TestMyModule.c:15:test_one:FAIL:a isn't 2, end of the world!

And then there is the elegant solution, using Unity's multitude of pretty assertions:

TEST_ASSERT_EQUAL_INT(2, a);
TEST_ASSERT_EQUAL_HEX8(5, a);
TEST_ASSERT_EQUAL_UINT16(0x8000, a);

Which, if run in separate tests, would lead to the following failures:

TestMyModule.c:15:test_One:FAIL:Expected 2 was 1
TestMyModule.c:23:test_Two:FAIL:Expected 0x05 was 0x01
TestMyModule.c:31:test_Three:FAIL:Expected 32768 was 1

Isn't that nice? The first argument is the expected value. The second argument is the value you are testing. It's printed clearly in a format that is most convenient to you, the test writer. In fact, Unity can handle all sorts of types, not just integers.

TEST_ASSERT_EQUAL_FLOAT( 3.45, pi );
TEST_ASSERT_EQUAL_STRING( "Attention, Dr. Surly", greeting );

It can even handle situations where you want a custom message added, where you want to check a full array, or both!

TEST_ASSERT_EQUAL_INT_ARRAY( expArray, actualArray, numElements );
TEST_ASSERT_EQUAL_INT_MESSAGE( 5, val, "Not five? Not alive!" );
TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE( e, a, 20, "Oh snap!" );

One or more of these lovely assertions go into each test. A test is just a C function that takes no arguments and returns nothing. By convention, it starts with the word "test" or "spec":

void test_FunctionUnderTest_should_ReturnFive(void) {
TEST_ASSERT_EQUAL_INT( 5, FunctionUnderTest() );
TEST_ASSERT_EQUAL_INT( 5, FunctionUnderTest() ); //twice even!
}

A single test file will usually have multiple tests. Most often, one test file is used to test all aspects of a corresponding C source file. This can be made most clear with a simple naming convention: Where do you find the tests for MadScience.c? In TestMadScience.c, of course!

Get Unity

Download ZIP

This ZIP will give you the latest snapshot off github: Source. Docs. Examples. Scripts. Everything you need to get unit testing!

Git It

git clone https://github.com/ThrowTheSwitch/Unity.git
git pull

(or go see unity on github)

steampunk_family_the_vonheadwigs.jpg

Getting Started

Let's do this! First, you need to download your tools. Very likely, you'll start with just Unity. But you may choose to use Ceedling or CMock too. If you're not sure, maybe you want to start by using our Decide-O-Tron 3000. If you opted for Ceedling, you can start there to learn more. Otherwise, let's learn a bit about making a Unit Test with Unity.

The smallest realistic Unit Test build you can do is a source file, a test file, and Unity. Compile all three and link them together. We'll start with a native built app, because they're usually the simplest to get started. If you want to migrate to a simulated target later, we'll walk you through that too.

Let's say we have a C file that we want to test named DumbExample.c. It looks like this:

#include "DumbExample.h"

int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c)
{
return (int8_t)(((int16_t)a + (int16_t)b + (int16_t)c) / 3);
}

It has a header file that looks like this:

#include <stdint.h>

int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c);

Then we make a test file TestDumbExample.c which checks for some basic things like rollovers and whatnot:

#include "unity.h"
#include "DumbExample.h"

void test_AverageThreeBytes_should_AverageMidRangeValues(void)
{
TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(30, 40, 50));
TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(10, 70, 40));
TEST_ASSERT_EQUAL_HEX8(33, AverageThreeBytes(33, 33, 33));
}

void test_AverageThreeBytes_should_AverageHighValues(void)
{
TEST_ASSERT_EQUAL_HEX8(80, AverageThreeBytes(70, 80, 90));
TEST_ASSERT_EQUAL_HEX8(127, AverageThreeBytes(127, 127, 127));
TEST_ASSERT_EQUAL_HEX8(84, AverageThreeBytes(0, 126, 126));
}

int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_AverageThreeBytes_should_AverageMidRangeValues);
RUN_TEST(test_AverageThreeBytes_should_AverageHighValues);
return UNITY_END();
}

So we have a test file which contains two tests. Each test has multiple assertions. If any of those assertions fail, that particular test should fail and we should move on to the next test. When done, it should output our results.

Let's build and run executable! Assuming we have gcc installed, we can use it for the first step, and then directly run the binary produced

gcc TestDumbExample.c DumbExample.c ./unity/src/unity.c -o TestDumbExample
./TestDumbExample

or slightly differently on Windows:

gcc TestDumbExample.c DumbExample.c unity/src/unity.c -o TestDumbExample.exe
TestDumbExample

Either way, your command prompt should output something like this:

testUnit1.c:21:test_AverageThreeBytes_should_AverageMidRangeValues:PASS
testUnit1.c:22:test_AverageThreeBytes_should_AverageHighValues:PASS
-----------------------
2 Tests 0 Failures 0 Ignored
OK

SO that seemed to work. Of course, it's going to be very tedious to do all of this manually. We don't live in the stone age, here. We have tools! So, your next step is to determine what tools you want to use. You can use the Decide-O-Tron to help figure that out, or just follow the links below: