How To Test Code in C?


When you develop software, sooner or later you will inevitably come up against the question of how to test it properly at all. What are the options and what should you watch out for?

Sourcecode in C is often tested through Automated Tests, e.g. Unit Tests. A Unit Test checks the result of a call to the Code under Test against an expected Value and either Passes or Fails. There are other types of test each with a specific purpose.

We will cover the most important aspects of testing software, especially C code, in this article. You will also learn about the different test types that I meantioned above.

Testing
Image by Muhammad Usman from vecteezy.comOpens in a new tab.

How To Test Code in C?

The following article will show you different aspects of Testing Code, in general but also specifically in C. We will cover automated Testing, Unit Tests, TDD, Organisation, Naming and also those mysterious testing Frameworks you may have heard of.

Testing, Unit Test and Test Driven Development (TDD) in C

Now let us clarify a few definitions and terms, which are often confused and mixed up in practice. Essentially, this involves the terms automated testing, unit test and test-driven development.

(Automated) Testing

Testing and Automated Testing are more generic terms for the whole concept of testing C code. There are many forms of testing and many names given, sometimes they mean the same. Examples are Acceptance Testing, Regression Testing, Unit Testing, Black Box Testing, White Box Testing, and Integration Testing, but you will find even more if you look for it.

Each type of test exists for a specific purpose, whether it’s to check that the user requirements have been met, that the program is fast enough, that it can handle large amounts of data, or simply to make sure the code is doing what it’s supposed to.

The “Automated” part means, that you as a developer don’t have to manually start the program, click buttons, enter values, etc. every time in order to test the Software, but that you run a program which does this for you.

Unit Test

A unit test, and its related category of unit testing, is a type of test used by developers to essentially ensure that the smallest possible units in the code deliver the correct result, without even knowing the implementation of how the code does it. Accordingly, unit tests are so-called black box tests.

So-called unit testing frameworks are often used to help with this, you can find out more about this in the relevant section below.

Test Driven Development (TDD)

TDD is a specific method of writing tests or creating a computer program. Test Driven means that you first write a test and then the corresponding “real” code. Specifically, TDD is divided into three phases, often referred to as Red, Green, Refactor.

Red means write a failing Test first. If you write a new Test for a feature that doesn’t exists, there will be no code that makes the Test pass. Always start with a failing test, for two reasons: First you make sure that the feature you want to implement really does not exists and second you make sure that the test is testing the right things. If you would have a green test and then write code how would you know that the new could made your test pass?

Red means writing code until the test fails. Not compiling is also a failing test. If you for example write a class name that does not exist you will then have to implement an (empty!) class to make the test pass before you write any more test code.

Green means write enough code to make the test pass. Beginners often make the mistake to want write good or beautiful or optimized code here, but that is not the goal. The goal is to make the test pass (Green) and then refactor with the safety net of having a passing test.

Refactor is the most important but also most overlooked part of TDD. As soon as you are Green on the tests, look if you can make the code easier to read, more performant, better structured, etc. But only do one change at a time and then check if all tests still pass. This way you can change your code without changing its behaviour.

Of course there is a lot more to cover on TDD as I can provide here, but I will write an article about it in the near future.

What About C Unit Testing Frameworks?

A Unit Testing Framework is (often) a library that provides functionality to generate and maintain automated tests, mostly Unit Tests. You can (and should at some point, at least for practice) program your own Testing Framework, but there are many that will get done what you need.

A main function is the so called ASSERT, where an expectation (value of a variable, return value of a function, etc.) is compared against the actual value that your code produced. If both values are equal, the ASSERT passes, otherwise it fails.

/* EXAMPLE of an ASSERTION in a Unit Test*/
const int expected_value = 4;
int actual_value = getValueFromRealCode();
ASSERT_EQ(expected_value, actual_value);

There are other helpful features in Unit Testing Frameworks, some are good for specific test requirements, others will help you organize your tests.

Overview of C Unit Testing Frameworks

In my article about the best Unit Testing Framework I already compared four Frameworks. In plain C you will find CUnitOpens in a new tab. and UnityOpens in a new tab. as the ones that are often used. There are, of course, others, but these two are widely accepted and used, so you may give them a try.

Don’t Dismiss C++ Unit Testing Frameworks

Just because you are programming in plain C it doesn’t mean that you cannot use a C++ Unit Testing Framework. Two of them I also covered in the article about the best Unit Testing Framework. Tests have to be written in C++ but the Code under Test can be plain C.

The most popular is definitely Google Test (or GTest)Opens in a new tab. and it is used by many developers. If you want more information about testing C code with GTest, you can read my other articles about it, e.g the Hello World in GTest. The other one is CppUTestOpens in a new tab. and is even used by Embedded C Developers, so definitely worth a look.

How To Organize Testing in C?

Organizing Tests is important, especially if you work in a team of developers. However, there are several Layers of Organization.

On top there is the organization of files and folders. Where do I put my test projects and files so that I will find them later and know what the tests want to accomplish.

Then there is the organisation of the tests itself, do I seperate Integration, Unit and Acceptance Tests, and how do I seperate each category, e.g. which Unit Tests come together (hint: Gathering by Feature is a good start).

Then we have the Tests itself. There are someguidelines most developers follow, like the AAA principle, when they write individual tests. AAA stands for Arrange Act Assert and describes the order in which code semantics is applied in the Unit Tests.

I have summarized a comprehensive insight into test organization in this article.

What is a Good Naming Convention for Tests in C?

Names matter, especially in Programming. Having a good naming convention for your tests is mandatory. It helps with the organizing part and helps you remembering what you wanted to test in the first place. As obvious as this may be when you write the tests, it isn’t this obvious anymore when you revisit your tests six months later.

There are some guidelines for naming conventions and some helpful hints, tips and tricks, and I covered a lot of them in my article about Naming Conventions for Unit Tests.

As an example, the Name of a Test may be written in CamelCase or CamelCase_with_Underscores like this:

/* Camel Case */
TEST(LoginWithCorrectPassword)
{
  /* TEST here */
}

/* CamelCase_with_Underscores */
TEST(Login_with_correct_Password)
{
  /* TEST here*
}

As is so often the case, there is no right or wrong here. The only important thing is to decide on a path and then to follow it consistently.

Does Test Coverage Matter in C?

Test coverage is the percentage of source code that is covered by unit tests. Beginners of unit testing think they have to achieve 100% test coverage.

As a rule, however, this is not necessary at all. Code which, for example, is used for GUI generation or should only be checked through integration tests will reduce the test coverage rate. The goal should be to test all features, not necessarily 100% of the code.

More information on test coverage is covered (pun intended) in my article on test coverage which you can read here.

Summary

This was a brief introduction on how to test C code. You see that it is possible and that it is not so hard. It is even easier if you use one of the Unit Testing Frameworks. If you want more informaion on the topic of Unit Testing in C, then feel free to read my article on XXX .

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