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
Figure 8: Test instantiation