5 Essential Macros to Build a Test Framework in C++ | by Avinash Ravi Shankar | Apr, 2022

Write a minimal unit testing framework in C++

Picture by writer

As a software program developer, it will be significant that you just write exams to verify in case your code does precisely what it’s alleged to do. You don’t need to be pushing defective code into manufacturing, particularly, if you didn’t account for a boundary situation, do you?

You may need come throughout common frameworks, just like the GTest, Catch, or BoostTest, in the midst of testing c++ tasks. They’re advanced but strong and let you check code in any approach you need.

Nonetheless advanced these frameworks could also be, the core ideas carried out for testing are, very a lot, related. And, on this submit, we’ll study the fundamentals by writing a easy framework ourselves.

Testing frameworks use macros to keep away from cluttering supply information with a ton of boilerplate code. For our framework, we’ll make do with simply 5 of them. A macro to declare, outline, register, and run check instances, and, after all, a macro to check outcomes with the anticipated values.

Macros are a intelligent technique to create check courses which can be distinctive to every check case. We’ll want two macros for the job. A DeclareTest, and DefineTest. And, since we’re doing object-oriented programming, we’ll make use of some inheritance and design patterns as effectively.

Macros to declare and outline check instances

There are three issues to level out within the code snippet above:

  1. The token pasting operator ## creates a novel check class for every distinctive Module or a TestName.
  2. The Singleton sample makes certain that there’s just one occasion of every case.
  3. And, to outline exams, we override the inherited runFunc() methodology from its father or mother (mentioned subsequent), the UnitTest.

The beneath pattern reveals check declaration and definition with these macros.

Check case declaration
Check case definition

The category UnitTest is central to this framework. It manages all of the instantiated check courses. To keep away from creating a number of managers, we make it a Singleton. It additionally supplies a digital runFunc() methodology that the inheriting courses can override to outline check instances.

Check out the next snippet:

UnitTest class declaration
  1. The getInstance() methodology returns a singleton.
  2. The variable isTrue shops the check consequence. Both true or false.
  3. The testList shops tips to all instances registered with this class.
  4. The expectEQ() methodology evaluates whether or not the anticipated values are the identical because the returned values from the unit underneath check. Since it’s a template methodology, type-checking is already accomplished for you. You will not evaluate apples to oranges.
  5. And, lastly, the runTests() methodology executes all exams saved within the testList.
runTests() definition

Static variables handed and failed hold observe of the outcomes of every check case executed. With the testList holding tips to the check instances, we will entry and execute the runFunc methodology straight from the UnitTest class.

Now, as soon as we’ve outlined and instantiated exams, we have to add them to the testList. We do that with the RegisterTest macro.

And, lastly, for comfort, we’ll outline macros to run and evaluate check outcomes.

Macros to register, run, and evaluate exams

Operating check instances in separate processes enable your program to run until the top. Even when certainly one of them throws an exception and aborts. Remoted check instances comprise the injury and stop your program from crashing.

Each Home windows and Linux methods enable creating separate processes. Home windows calls it CreateProcess, whereas, within the Linux world, we name it fork.

Right here’s a code snippet that forks the runTests methodology on a Linux/Unix system.

Every case within the testList runs in a brand new course of created by the vfork() system name. It returns a optimistic quantity because the ID of the forked course of and assigns the ID 0 to the kid. Our runFunc methodology is then referred to as within the forked course of, isolating it from the remainder of this system.

Now, you may debate on whether or not to fork() as a substitute of vfork(). However, I choose the latter since it really works with the static variables, and prints console logs in the precise order.

We’ve created a testing framework that’s, minimal and easy to make use of. With simply 5 macros, you may write exams with out involving your self with the small print of the framework. It might’t get any less complicated!

Pattern check program

I hope this was helpful and informative to you. Try the entire supply code on my GitLab account.

Need to Join?Join with me on Twitter for all the newest updates!

More Posts