At some point in your developer career, you might face the unpleasant task to write code tests for an already implemented feature of your app which was never tested before. At first, you get started right away but then you might realize that the business logic and the framework code are tightly coupled. At that point, writing tests look like a painful task. So what are you doing?
There are different ways to tackle those issues. In this first part, I want to look at one possible option to make untestable code testable again. Following points will be discussed:
In essence, you cannot write automated code tests for a function of your app because it is tightly coupled to components or libraries which are out of your control. This can be GUI components, asynchronous methods or a 3rd-party library.
Here is a simplified example of untestable code in .NET:
I'm using code-behind of a WPF app to calculate the cross sum of the input. When trying to write a unit test for the CalculateCrossSum method you would usually mock you dependencies with some mocking framework like moq or RhinoMocks. Unfortunately, it is not possible to mock MessageBox.Show for a Unit Test. Due to tight coupling between business logic and UI components, there is no way to implement an automated Unit Test for the CalculateCrossSum method.
As shown above, untestable code reduces the overall test coverage of your application. Of course, you don't write tests to achieve 100% test coverage. Your tests need to be useful, and they are supposed to test the functionality of your app. The less source code is tested, the higher the chance of introducing new bugs into your software. The confidence in your code reduces, and it is more difficult to refactor the source code in the long-run. Ultimately the code quality reduces over time, and implementing new features gets almost impossible.
Staying with the WPF example above, you want to make sure that the current functionality of our app is not changed when refactoring the code or implementing new features. If we do not have automated Unit Tests to verify the basic functionality of our app, how can you make sure that everything works as expected? Of course, the above example is very simplified, but this is also the case when talking about big enterprise application. In the end, the last option is verifying business-rules manually. This can be very costly, and it takes a lot of time.
Further, I want to show extraction of logic from hard-to-test components into testable components.
Below is shown a basic C# example of a service that contains a method called CreateSuperHeroAsync. It is an asynchronous method, and the business logic is kept very simple for demonstration purposes.
Now we want to create a Unit Test for the CreateSuperHeroAsync method. One major issue here is the unpredictability due to the asynchronous method call and the static Thread.Sleep call. We also want to keep the System.IO.File.AppendAllLinesAsync callout of our business logic. How to test the business logic without losing the asynchronous functionality of the overall method?
Gerard Meszaros talked in his book "xUnit Test Patterns" about the Humble Object pattern. It is part of the Design-for-Testability Patterns, and it is similar to the Adapter pattern. Even though the purpose is different. The Adapter pattern converts the interface of a class into another interface that a client expects.
On the other side, the Humble Object pattern, extracts the logic out of the hard-to-test components into a testable module. You implement a service interface that contains methods that expose the business logic of untestable code. As a result, you get a thin adapter layer that consists of almost no code.
We refactored the SomeService class and extracted the logic from the CreateSuperHeroAsync method into a separate C# class. Thus we get a True Humble Object. We inject the interface of this class into SomeService and call the method CreateHeroes inside the CreateSuperHeroAsync class.
So what is the advantage? We are now able to test our business logic synchronously and verify the Humble Object if we need to. Thus it makes the Humble Object pattern very powerful when splitting the hard-to-test behaviors and the testable behaviors.
The Humble Object pattern gives us the change to divide testable and untestable code. With this pattern, you can even test the hard-to-test part without worrying about the "moving parts" inside your system undertest. To recap the key takeaways:
In part two of this series, we take a closer look at decoupling your code from the UI by inspecting different architectural patterns. So stay tuned, and in the meantime, let me know what you think about the Humble Object pattern.
A Testing API can help you decrease the structural coupling between your production code and your test code. In the long run, it will save you much time! A bold claim, of course, and it sounds very theoretical. To provide you with more depth than just a basic concept, I want to show you today how you can implement a Testing API with .NET.
Structural coupling makes refactoring our code very difficult and it costs us a lot of time and money! But there is one solution: a Testing API. It's an API with superpowers which helps you to get rid of this structural coupling. At the same time you are still able to verify your business rules. This can be a real time-saver!