How To Organize Unit Tests in C? (Files, Fixtures & AAA)


Writing unit tests can be hard work. What can help you is the right organization and a structured approach. But what is the best way to organize Unit Tests and what exactly is the AAA pattern?

Package unit tests into their own project, one feature per file. Group this feature into fixtures or groups within the file. Implement your tests using the AAA pattern. Don’t test methods, test behaviors.

The rest of this article goes into more detail on how best to organize unit tests, both at the file and code level. This is something every developer who writes unit tests should be concerned about.

How Do You Organize Unit Tests in C?

There are many Test Frameworks (find out here which one is the best Unit Test Framework in C for you), but by and large they all have the same structuring.

It is important that the unit tests are well organized. On the one hand, this applies to the file level, i.e. where the test files are saved and how they are named. But this also applies to the tests themselves and the way in which they are implemented.

Unit tests should be small, fast, and easy to read. The organization of the tests should serve this purpose alone.

Should Unit Tests be in a Separate Project?

I would highly recommend to put all of your test code into a separate project. Although it is not mandatory it helps to keep your production code clean. Also many frameworks require your main function to call the test specific things so you would have to comment that in and out or use special defines and configurations which is not very practicable.

Files and Folders – Where Do Unit Tests Go in a Project?

If you follow the advice from the last paragraph and have your own project for your tests, then only test-related code will exist in that project. If you use folders or filters to organize your code, I would recommend storing tests separately from helper functions or mocks and stubs, if any.

Below is an example of a folder structure for tests. Since the project is at the very beginning, the production code is still included in the test project. However, this will change within the next iterations and the production code will be completely moved out of the test project.

Unit Test Organisation Example
Example of a Folder Structure in for a Unit Test Project with Filters in Microsoft Visual Studio 2019

For files, you should have one feature per file. The name of the file would then be the description of the feature, possibly with the suffix ‘Test’, for example CalculateNumbersTest.c.

Unit Test Grouping, Setup and Teardown

Within a test file, you could easily implement all tests for a feature independently. Most of the time, however, it makes more sense to group these tests together. On the one hand, they can use it to document the various aspects of the feature by means of simple naming. On the other hand, you have the possibility to define setups and teardowns that have to be done before and after each test once.

Depending on the Unit Test Framework you are using, this grouping will have a different name. Common names are Fixture (xUnit Frameworks), Group (Unity Framework) or Class (Google Test). However, what is meant is the same. It is important that you only have one Fixture/Group/Class per file and feature.

Usually there are also templates for SETUP and TEARDOWN methods, which are then automatically called in the test framework itself before and after each individual test.

Example

/* Test Group in Unity */
TEST_GROUP(LedDriver);

TEST_SETUP(LedDriver)
{
	/* Add Setup Code here */
}

TEST_TEAR_DOWN(LedDriver)
{
	/* Add Clean Up Code here*/
}

(Unit) Tests

If the whole file describes a Feature of your production code, then what does your test stand for? It is a distinct behavior of your software. This can be a function or class, but it does not have to. This is the part most beginners get wrong because they think they have to write a test for each function desperately which isn’t necessary true.

When it comes to naming you test you should stick to some sort of naming convention consistently throughout your whole project(s). You can read about different naming conventions and their pros and cons in an article I wrote here.

Now we will take a look into what to write within the Unit Test. Notice that we are going inside out, coming from the project, the file, the fixture, the test and now down to the detail of the test content.

Arrange, Act, Assert – What is the AAA Pattern?

The AAA pattern describes a procedure for creating a test. The test is mentally divided into three sections, with each ‘A’ standing for a part, namely Arrange, Act and Assert. Each section is described in more detail below.

Arrange

Everything that is required to carry out the test is prepared in this section. In OOP languages, for example, this would be the creation of all required objects. In C it is the declaration of all required variables and the setting of values ‚Äč‚Äčthat are necessary for the respective test.

You could also prepare a in memory database or fake some results from functions or create a stub. In contrast to the fixture setup, however, the setup here only applies to the current test. This section is usually the biggest of the three ‘A’s.

Act

The ‘Act’ section should be very short if not the shortest of the three sections. The code that provokes the result or changes a state is executed here.

The area should not be complex and if possible should not contain any if statements or loops. Remember that a test should be easy to read. One should be able to see at a glance what is being tested, what result is expected, and how that result is achieved.

Assert

Finally, the test results are validated. In the case of smaller tests, this is usually just one line, which executes an ASSERT. However, several ASSERTs can also be carried out here. While you should only check one thing per test, that doesn’t necessarily mean there’s only one ASSERT command.

For example, if I want to check whether several parameters have been passed correctly, I need an ASSERT per parameter. As long as the reason for a change or failure is the same, I can group the ASSERTs into one test.

In addition, the test-specific teardown takes place here. In C, for example, it could be freeing up dynamically allocated memory using ‘free’. Similar to ‘Arrange’, the teardown here only applies to the current test.

Example

/* A Test Case in Unity */
TEST(Calculate, SumOfTwoNumbers)
{
  /* Arrange */
  const int a = 3;
  const int b = 2;
  int result = 0;

  /* Act */
  result = sum(a,b);

  /* Assert */
  TEST_ASSERT_EQUAL(5, result);
}

Now you know how to organize your Unit Tests. This should make your project clearer and help you achieve higher code quality.

This article was first published on moderncprogramming.com. If you are reading this somewhere else, it may be plagiarism.

Marco Lieblang

Professional Programmer since 2003, passionate Programmer since the mid 90's. Developing in many languages from C/C++ to Java, C#, Python and some more. And I also may know a bit about Assembly Languages and Retro Systems.

Recent Posts