Still today, we see often testability as the necessary evil. Designing software architecture in a way that you can test your code upfront is seen as self-evident but in reality this is rarely the case. Finding the right architectural pattern really depends on your business requirements. And testability and code quality is not always the main priority, especially when you have a tight schedule. But how can we provide a more testable design without sacrificing time-to-market?
We usually tend to implement our applications in a layers or divide it into different components. But what exactly is a layered architecture? Are there different types of layered architectural patterns? And how can we structure our application to support more testability? These questions I want to answer in this first part of my new series "Design For Testability".
Layered architecture in the object oriented programming paradigm defines a system as a complex entity that can be decomposed into interacting layers. They are highly decoupled components which communicate among each other through interfaces.
Traditional layered architecture patterns always try to separate UI, business logic and the database. These areas can be seen as layers which themselves consists of several services or components. All layers are tightly coupled with the infrastructure of an application. Usually these layers have a vertical hierarchy where high-level components depend on the low-level components functionality.
But this is not absolutely necessary! The components are actually completely independent of each other and layered architectural patterns do not necessarily consist of a UI. A higher level layer can be any kind of client that consumes information from a lower level layer.
There are great examples of architectural patterns which try get rid of the hierarchical structure and make use of the Dependency Inversion principle, like the Onion Architecture or the Hexagonal Architecture. Besides the two patterns, mentioned above, there are also other well known types of layered architectural patterns are of course MVC, MVVM, PAC or Microkernel.
So why should a layered architectural pattern suite more testability? Well, it is easier to isolate the system under test when creating unit tests. Each layer provides explicit and stable interfaces for other layers. The implementation details are internal and not visible for outside components. Therefore we avoid tight coupling and interdependencies. Therefore it is easier to test components independently.
Of course, you can do that with many different architectural patterns as well. But if we focus on non-hierarchical approaches, like the Onion Architecture, the main advantage of layered architectures is that our tests can be seen as a separate layer as well. This testing layer can contain all kind of automated code testing, like:
Our tests have full access to our application and domain logic due to the dependency inversion. We have a high degree of freedom for creating stable and clean tests.
It really depends on your use case and your chosen architectural pattern. But for object oriented programming languages, it is always useful to keep your tests in a separate test project.
Keeping your tests all at one place makes it easier to find them by Testing Frameworks. Most of modern testing frameworks are pretty good in discovering your tests and all modern IDEs provide some kind of Test Explorer to give the developer a better overview of all your tests.
Let's say, we have to build a business application called, FooBusiness App. So eventually, you could structure your application like this:
Depending on your use case and your architectural pattern, you can have much more folders, of course. Each folder is one layer. Each layer can contain more folders (feature folders) which consist of classes and interfaces. Of course, you have to be careful with your naming so other developers can understand your intent.
Even though Layered architectural patterns have some drawbacks in terms of scalability and development time, it is always useful to structure your application that it suites your business requirements. So here are my key takeaways:
Let me know what you think and stay tuned for part 2 of this series. If you can't wait, you can also check out my video on "Design for Testability".
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!