Compile the C test runner

Compile the C test runner

Part 1 of exploring the Unity testing framework to do TDD in C.


A minimal starting point

Let's get a Unity test runner to compile!

The Concept: Reduce code complexity

As outlined in TDD in C: Part 0, I won't try to anticipate what the next piece of missing boilerplate, filesystem support, tooling or user code may be. Primarily, I will let build and test failures guide development instead of hunches about upcoming requirements or 'necessary' complexity.

💡
When the goal is to achieve a required functionality with the least amount of code complexity, treat code as a liability.

Applying the concept

What does the concept of focusing on satisfying a requirement with the least amount of code complexity imply for our current project? The answer is quite simple: we try to use the least amount of code complexity possible to get a TDD workflow going in C.

What is a key part of a TDD workflow in C? A test runner. Well, what does the Unity framework's test runner need 'to get going'? Since the Unity framework is pure C, a Unity test runner must have an entry point under the hood somewhere: a main function.

The Unity documentation says the following boilerplate is the entry point for a test runner:

What if we try using a test file that contains just that main function? We suspend thoughts about whether it is sufficient to compile, whether our dependencies are installed, etc and take this as a minimal starting point.

💡
When we have a first principle such as 'A Unity test runner is a C program so it must have a main entry point', we can use GitHub's search feature to find candidate starting points: repo:ThrowTheSwitch/Unity "int main("

The Commits

feat: add Unity to project

👆
Observe: Each commit is linked to by the header of the section that discusses it.

I'm starting with an empty repository: TDD-with-Unity.

1. I create test_that_unity_works.c. This is my test file. It has the main function.

2. I attempt to compile:
gcc test_that_unity_works.c -o unity_test_runner.out

Note: I am building on Ubuntu.

Unsurprisingly, I get warnings and errors that need to be fixed.

test_that_something_happens caused an 'undeclared' error.
The macros UNITY_BEGIN, RUN_TEST and UNITY_END caused implicit declaration warnings.

3. I stage and commit the change.

fix: declare test function

At this point, all I want is for my minimal unity test runner to compile. I don't need to anticipate implementation details beyond that. If a change is sufficient to remove an error or warning, it is enough.

1. I add an empty function to test_that_unity_works.c.

2. I check to see if I am one step closer to compiling:
gcc test_that_unity_works.c -o unity_test_runner.out

I still get the warnings about the test suite management macros.

The macros UNITY_BEGIN, RUN_TEST and UNITY_END caused implicit declaration warnings.

But I no longer get the 'undeclared' error. I've also progressed along the compile toolchain - I have a linking error now instead:

There are undefined references to the UNITY_BEGIN, RUN_TEST and UNITY_END macros.

We are one step closer.

3. I stage, commit and push:

💡
On reflection, a simpler starting point would have been a main with just UNITY_BEGIN and UNITY_END - just the test runner macros. Not a test runner and test cases.

chore: add Unity as submodule

1. From the repository root, I add the Unity framework as a submodule:
git submodule add https://github.com/ThrowTheSwitch/Unity.git Unity

Unity is actually three files: unity.c, unity.h and unity_internals.h. Although the Unity repository contains more than that (documentation, plugins, examples, self-tests, and so on) and there are quality-of-life script options available, core Unity is those three files and nothing else. The macros will be in them.

2. I confirm that the Unity dependencies are in the filesystem:
find -name unity.c -o -name unity.h -o -name unity_internals.h

3. I stage, commit and push.


4. In a different, non-nested directory, I use git clone --recurse-submodules to check that the repository will supply a collaborator with the Unity dependencies:

I expect the find command to return three entries and it does.

💡
When I cannot validate behaviour with an automated test, I do a manual test.

docs: add clone How To

1. I add a quick note to the README.md about how to clone the project.

💡
Instead of assuming everyone will know how to use a submodule (which I only learned about recently), I document how.

fix: include Unity dependencies

I now move on to fixing the implicit declaration warnings for the test suite management macros.

1. I add #include "unity.h" to test_that_unity_works.c.

2. I attempt to compile:
gcc -I./Unity/src test_that_unity_works.c ./Unity/src/unity.c -o unity_test_runner.out

The linker cannot find references for setUp and tearDown.

Great. We have eliminated the warnings about the test suite management macros. A linker error has replaced it. This is progress.

3. I stage, commit and push.

fix: define setUp and tearDown

Again, I focus on defining only what is needed to make the code compile.

1. I define tearDown and setUp in test_that_unity_works.c with empty function bodies.

2. I attempt to compile:
gcc -I./Unity/src test_that_unity_works.c ./Unity/src/unity.c -o unity_test_runner.out

There are no warnings and no errors. unity_test_runner.out compiles and runs.

3. I stage, commit and push.

💡
We don't have any real tests running yet. That will be the next programming task.

The Replit

This is the state of the project after doing all the commits in this blog post. When compiled, the file test_that_unity_works.c becomes the test runner. It will execute the tests defined in the file so long as those test functions are included in the main function as an argument to a RUN_TEST macro.

Next: A Deep Dive

The next article in the series will be a deep-dive on the test suite management macros (BEGIN_UNITY, RUN_TEST and UNITY_END).


👋
Hello, I'm Warren. I've worked in an AWS Data Engineer role at Infosys, Australia. Previously, I was a Disability Support Worker. I'm interested in collaborative workflows and going deeper into TDD, automation and distributed systems.
📆
I am currently studying C at Holberton School Australia.
🐴
"Holberton School Australia is a tech school that trains software engineers through a collaborative, project-based curriculum. Over the course of 9 months, our students learn how to walk, talk, and code like software engineers."