Unwanted dependencies are a critical problem in software development. We often have to break existing dependencies before we can change some piece of code. Breaking existing dependencies is also an important preliminary to introduce unit tests for legacy code — according to Feathers definition code without unit tests.
Feathers’ seams help in reasoning about the opportunities that exist when we have to break dependencies. The goal is to have a place where we can alter the behaviour of a program without modifying it in that place. This is important because editing the source code is often not an option (e. g., when a function the code depends on is provided by a system library).
Feathers characterises a seam as a place in our code base where we can alter behaviour without being forced to edit it in that place. This has the advantage that we can inject the dependencies from outside, which leads to both an improved design and better testability. Every seam has one important property: an enabling point. This is the place where we can choose between one behaviour or another. There are different kinds of seam types.
C++ offers a wide variety of language mechanisms to create seams. Beside the classic way of using subtype polymorphism which relies on inheritance, C++ also provides static polymorphism through template parameters. With the help of the preprocessor or the linker we have additional ways of creating seams.
Object seams are probably the most common seam type. To start with an example, consider the following code where the class
GameFourWins has a hard coded dependency to
According to Feathers definition, the call to
play is not a seam because it is missing an enabling point. We cannot alter the behaviour of the member function
play without changing its function body because the used member variable
die is based on the concrete class
Die. Furthermore, we cannot subclass
GameFourWins and override
play is monomorphic (not virtual).
This fixed dependency also makes
GameFourWins hard to test in isolation because
Die uses C’s standard library pseudo-random number generator function
rand is a deterministic function since calls to it will return the same sequence of numbers for any given seed, it is hard and cumbersome to setup a specific seed for our purposes. The classic way to alter the behaviour of
GameFourWins is to inject the dependency from outside. The injected class inherits from a base class, thus enabling subtype polymorphism. To achieve that, Mockator provides a refactoring called Extract Interface and creates the following code:
This way we can now inject a different kind of
Die depending on the context we need. This is a seam because we now have an enabling point: The instance of
Die that is passed to the constructor of
Although object seams are the classic way of injecting dependencies, we think there is often a better solution to achieve the same goals. C++ has a tool for this job providing static polymorphism: template parameters. With template parameters, we can inject dependencies at compile-time. We therefore call this seam compile seam.
The essential step for this seam type is the application of a the refactoring extract template parameter. The result of this refactoring can be seen here:
The enabling point of this seam is the place where the template class
GameFourWinsT is instantiated.
The use of static polymorphism with template parameters has several advantages over object seams with subtype polymorphism. It does not incur the run-time overhead of calling virtual member functions that can be unacceptable for certain systems. Probably the most important advantage of using templates is that a template argument only needs to define the members that are actually used by the instantiation of the template (providing compile-time duck typing). This can ease the burden of an otherwise wide interface that one might need to implement in case of an object seam.
C and C++ offer another possibility to alter the behaviour of code without touching it in that place using the preprocessor. Although we are able to change the behaviour of existing code as shown with object and compile seams before, we think preprocessor seams are especially useful for debugging purposes like tracing function calls. An example of this is shown next where we trace calls to C’s
malloc function with the help of Mockator:
The enabling point for this seam are the options of our compiler to choose between the real and our tracing implementation. We use the option
-include of the GNU compiler here to include the header file
malloc.h into every translation unit. With
#undef we are still able to call the original implementation of
Beside the separate preprocessing step that occurs before compilation, we also have a post-compilation step called linking in C and C++ that is used to combine the results the compiler has emitted. The linker gives us another kind of seam called link seam. We show three kinds of link seams here:
In this type of link seam we make use of the linking order. The linker incorporates any undefined symbols from libraries which have not been defined in the given object files. If we pass the object files first before the libraries with the functions we want to replace, the GNU linker prefers them over those provided by the libraries. Note that this would not work if we placed the library before the object files. In this case, the linker would take the symbol from the library and yield a duplicate definition error when considering the object file. Mockator helps in shadowing functions and generates code and the necessary CDT build options to support this kind of link seam:
The order given to the linker is exactly as we need it to prefer the symbol in the object file since the library comes at the end of the list. This list is the enabling point of this kind of link seam. If we leave
shadow_roll.o out, the original version of
roll is called as defined in the static library
libGame.a. This type of link seam has one big disadvantage: it is not possible to call the original function anymore. This would be valuable if we just want to wrap the call for logging or analysis purposes or do something additional with the result of the function call.
The GNU linker ld provides a lesser-known feature which helps us to call the original function. This feature is available as a command line option called wrap. The man page of ld describes its functionality as follows: “Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to
__wrap_symbol. Any undefined reference to
__real_symbol will be resolved to symbol.”
As an example, we compile
GameFourWins.cpp. If we study the symbols of the object file, we see that the call to
Die::roll — mangled as
_ZNK3Die4rollEv according to Itanium’s Application Binary Interface (ABI) that is used by GCC v4.x — is undefined (
U for undefined symbols).
This satisfies the condition of an undefined reference to a symbol. Thus we can apply a wrapper function here. Note that this would not be true if the definition of the function
Die::roll would be in the same translation unit as its calling origin. If we now define a function according to the specified naming schema
__wrap_symbol and use the linker flag
-wrap, our function gets called instead of the original one. Mockator helps in applying this seam type by creating the following code and the corresponding build options in Eclipse CDT:
To prevent the compiler from mangling the mangled name again, we need to define it in a C code block. Note that we also have to declare the function
__real_symbol which we delegate to in order to satisfy the compiler. The linker will resolve this symbol to the original implementation of
Alas, this feature is only available with the GNU tool chain on Linux. GCC for Mac OS X does not offer the linker flag
-wrap. A further constraint is that it does not work with inline functions but this is the case with all link seams presented here. Additionally, when the function to be wrapped is part of a shared library, we cannot use this option.
If we have to intercept functions from shared libraries, we can use this kind of link seam. It is based on the fact that it is possible to alter the run-time linking behaviour of the loader
ld.so in a way that it considers libraries that would otherwise not be loaded. This can be accomplished by the environment variable
LD_PRELOAD that the loader
With this we can instruct the loader to prefer our function instead of the ones provided by libraries normally resolved through the environment variable
LD_LIBRARY_PATH or the system library directories. As an example, consider the following code and the CDT build options which is generated by Mockator to intercept function calls to
The advantage of this solution compared to the first two link seams is that it does not require re-linking. It is solely based on altering the behaviour of
ld.so. A disadvantage is that this mechanism is unreliable with member functions, because the member function pointer is not expected to have the same size as a void pointer.
Although there are already various existing mock object libraries for C++, we believe that creating mock objects is still too complicated and time-consuming for developers. Mockator provides a mock object library and an Eclipse plug-in to create mock objects in a simple yet powerful way. Mockator leverages the new language facilities C++11 offers while still being compatible with C++98/03.