Thursday, September 6, 2012

Testable C++ using Seams

Seams and C++

Writing testable code or making existing code testable is not easy. To do this you have to understand how to decouple the code from what makes it not testable. Michael Feathers introduced the concept of seam to explain how to make code testable in Working effectively with legacy code. According to him a seam is a place where you can alter behavior in your program without editing in that place. In other word, a seam is a place where you can use production collaborators (objects, functions) in your production project and fake collaborators in your test project. It is a test enabling point.

In his book, Michael explains that there are different types of seams depending on the language you are using. In C++, possible seams are the preprocessing seam (use the preprocessor to change behavior in production code or test code), the linking seam (use the linker to link to different behavior in production code and test code) and the object seam (instantiate different object in production code or test code). Object seam, preprocessing seam and linking seam are all described in this article.
In this article I will show how C++ seams can make a piece of code testable. I will use the preprocessing seam and introduce one seam that is not often mentioned for C++: the template seam. The template seam consists in using a template class or a template method as a testing enabling point. I will take an example inspired from my experience to illustrate these seams.

The Class under Test

In this example we want to test a class which is inheriting from an API Class (see Figure 1). We cannot test this class directly because the API class (see Figure 2) is an awkward collaborator: It is throwing exception when running in a test project because it cannot connect to a database (see Figure 3).


Figure1:Class Under Test 

Figure 2: API Class 

 Figure 3: Test

The preprocessing seam

A first solution we can use to make the class testable is the preprocessing seam:
Figure 4: Preprocessing Seam

Figure 5: Fake API Class

As you can see in Figure 4, the preprocessor includes a different header and modifies the source code when UNIT_TESTING is defined. If we compile CMyClass in a test project which defines UNIT_TESTING, it will inherit from a IAPIModuleImplFake (see Figure 5) and will be testable. In the production project, which does not define UNIT_TESTING, CMyClass remains unchanged because it still inherits from the IAPIModuleImpl API class.

The template seam

In C++ another technique,specific to C++, that provides test enabling points is templates. We can put CMyClass’ logic in a template class called CMyClassLogic (see Figure 6).
Figure 6: CMyClassLogic 

CMyClass can become an instantiation of the template using the API class in the production project (see Figure 7):
 Figure 7: Production Instantiation 

In the test project we can use the following instantiation of the logic :
 Figure 8: Test instantiation 

In the test project, CMyClassLogic is instantiated using the fake API (see Figure 8) and will thus inherit from the fake API.All the logic and interactions with the API can be tested using the fake to check and simulate interactions in the test project. In the production project, CMyClass is an instantiation which still interacts with the production API because CMyClassLogic is inheriting from the API when it is instantiated with the API.

Conclusion

There are other solutions to test this class and other seams to use. C++ provides object seam through virtual methods and interfaces, and linking seam in addition to the preprocessing and template seam. Each language has its own seams. To find them follow Michael Feathers’ piece of advice:” Think about all the steps involved in turning the text of a program into running code on a machine”.