Jonathan Boccara's blog

FSeam: A Mocking Framework That Requires No Change In Code (Part 1)

Published July 2, 2019 - 0 Comments

In this guest 2-post series, guest writer Quentin Balland presents us FSeam, his mocking framework to make code testable without losing readability because of mocks. Quentin is is a French C++ developer eager to learn and share his knowledge on his free time. You can find Quentin online on @FreeYourSoul68 and on his blog.

FSeam is a mocking framework I developed to answer to some problems I had while trying to test code in the FyS project. You can find the code here. We will come back shortly to this framework and explain how it works after speaking a little bit about why I actually decided to work on this.

The others mocking frameworks

googletest

Before talking about FSeam, it is needed to talk about the other frameworks that exist (GMock being the most famous one by far). The base of those frameworks is kind of following the same principle as Mockito does in Java. Basically depending on dependency injection where the dependency implementation injected is different in a testing binary than in production one.

In Java, some frameworks like Spring give you inversion of control mechanism by having the framework injecting instances of classes. Which makes you able to “ask” the framework to inject fake implementations of your class in testing context. This is a rough explanation, but it basically what it does. Those fake implementations follow the same interface than the production implementation. And it works fine.

Unfortunately, such mechanism has issues in C++.

Dependency injection: Inheritance

The easiest way to manage a dependency injection in C++ is to send an interface to a function/class in order to take advantage of dynamic polymorphism by changing the class that implements the interface at runtime (using testing implementation when in a test, and normal implementation when in the production code).

This implicitly means that the code is going to use inheritance. In Java, it is a normal thing to do and it doesn’t change anything in everyday Java code as the usage of interface is recommended and very common in Java.

But in C++, working with inheritance implies virtual functions. And virtual functions have a cost, which means that most of the time, you are going to pay for the price of a virtual just in order to make your code to be testable. This is one of the problems that FSeam can resolve.

Another important thing to note is that this kind of dependency injection forces you to add arguments for each dependency you could have. Which can be quite a few in some cases.

Dependency injection: Templates

Another possible solution in C++ would be to have the type dependencies of your class/function into templates.

With this method you have more possibilities to inject your dependency:

You can do like with inheritance and send them as parameters (template parameters resolved at compile time).

Or, you can simply create an instance of the templated object as a member of your class., Iif you do so, you would need a getter on this member variable in order to be able to modify its behavior by calling ON_CALL with GMock for instance.

Google Mock makes it possible to create a standalone mocked object without using inheritance or virtual methods. The google mock type can be passed as type into a template. But the mock instantiated must be retrievable by the user of the class in order to change its behavior via the ON_CALL macro (we will see how FSeam doesn’t require such refactoring).

Another problem that can occur with this solution is that in some cases, a big load of template can be necessary in the code to cover all the dependencies (this issue may be mitigated by having a class regrouping all/a part of the dependencies class, but it implies additional types to be created and may produce confusing and hard to read code). This is nonetheless the way to go in general, as it is easy to setup and make you able to use commonly known mocking frameworks (GMock).

The un-testable code: legacy code

But unfortunately, some dark code exists in this world. Everyone has seen the 1000 lines of code that contains several dependencies that you need to get rid of in order to unit test the feature.

And I think everyone just dropped this function and said “Meh, it works since ages, no need to bother testing it”. And I can’t blame you as this thought is literally the one I had in front of such code.

But what if you need to test it? Or let’s just assume that you are a masochist and want to unit test the feature anyway? It would be hard or impossible with the above explained dependency injection techniques to make it work without impacting too much of the legacy code. And doing a refactoring of such code is often not recommended as you could have unpredictable impact on your business.

What is FSeam’s answer?

In summary, FSeam will try to resolve several of the typical difficulties of  testing:

  • Testing code without having to use virtual methods
  • Testing code without having to use a code bloating template
  • Testing legacy code without impacting the current code

Untouched code

FSeam doesn’t require upfront code refactoring. You don’t need to have the entirety of your code thought in a testing way. Even though testable code tends to be better designed than anarchic code, some legacy code out there hasn’t been thought for testability.

On top of that, FSeam makes it possible to mock static and free functions also without any code modifications. Google mock makes it possible. But it is difficult to achieve in practice and needs specific syntax.

With FSeam, the syntax to mock a class or a free function is kept the same as you basically end up manipulating the same objects that represent a mocking handler of your class or function.

Legacy testing

With FSeam, it is possible to test legacy features with little to no refactoring by just seam mocking database access class for instance. It is possible to give a default behaviour and monitor the default behaviours of the FSeam mocks.

Which means (in opposition with the template dependency injection) that you don’t need to implement additional getter methods in order to retrieve the actual instance of the mock and alter its behaviour (by calling ON_CALL on it with GMock for instance) and verify its usage.

But how does this actually work?

This is quite simple, FSeam is going to mock your class or method by generating a different implementation of your code at compilation time and compile it instead of the actual implementation.

The new implementation will call some internal functions of the mocking library in order to be able to manipulate the behaviours of the mocks via a header only library that you will include into your testing files. Michael Feathers called this mocking mechanism “link seam”, hence the name of the library.

In the next post, we’ll dive into the technical details of FSeam and its three main components: a code generator (made in python), a CMake helper function that will do the compilation tricks for you, and a C++ header only library to manipulate the mocks.

More details and examples to follow in the next article!

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin