A Testing Perspective of Controllers and Orchestrators

Comments 0

Share to social media

This article focuses on the testability of ASP.NET MVC controllers and suggests that, if you keep your controllers super-thin and move the logic to services and model, then you have no need to unit-test controllers and probably don’t need to mock the HTTP context that much. The article is natural follow-up of a previous article that appeared on Simple-Talk… Never Mind the Controller, Here is the Orchestrator.

From RAD to Unit Testing

ASP.NET MVC is inherently testable. Testability, the degree to which software inherently supports testing, has been recognized as a fundamental attribute of software since the first draft of the international standard ISO/IEC 9126 paper about software architecture. The first draft of this paper dates back to 1991. However, Design for testability didn’t seem to concern the average .NET developer much until 2004. Whatever the reason for its slow take-up, the success of .NET as a platform brought many companies to build more and more line-of-business applications, thereby dumping an incredible amount of complexity and business rules on development teams. Developers had to hurriedly change their approach to software development: Development needed to be rapid, but also reliable and extensible. It was becoming increasingly important to be able to design software in such a way to make it easy to test, and in particular to test automatically. Automated tests can give you a mechanical way to check edge cases and figure out quickly and reliably whether changes have broken existing features.

Testing Applied to Controllers

When the long-awaited ASP.NET MVC was introduced, the framework made it possible for website developers to practice unit testing. A lot of tutorials have been posted to show you how to write unit tests around controllers. These tutorials assume that the controller is where you serve the request and coordinate access to backend layers such as the data access layer. If the controller is the nerve center of your ASP.NET MVC application then, once you can artificially create a HTTP context to simulate requests and responses to mock up the HTTP context, then you’re pretty much done. Because ASP.NET MVC provides facilities to mock up the HTTP context, you are already provided with a clear and clean path ahead: You just need to write a bunch of unit tests for any of your controller classes and you’ll be fine. Is this really true?

Should You Test the Controller?

Testing is much easier if you can rely on clean code that is well separated in layers. Is the controller one of these layers? That question may surprise you if you, as an MVC developer, assume that the controller is the centralized machinery that governs the activity of an ASP.NET MVC site. The controller isn’t really one of the architectural layers of an ASP.NET MVC site: more accurately, the controller is an essential element that’s hard-coded in the framework. The controller is part of the infrastructure and therefore not a part of your code. The controller merely serves the purpose of receiving requests and dispatching them to other application specific services. in a previous article of mine for Simple-Talk, I named these services as orchestrators. An orchestrator is a helper component that coordinates any activity related to a specific request. Ideally, an orchestrator is invoked by a controller but operates independently. This means that it receives from the controller any data that it needs to work on and returns to it any calculated data.

in this model, the controller is merely a pass-through layer with no significant logic that really needs testing. The real business is taking place in the orchestrator and so it is the orchestrator component where you will want to focus your testing efforts.

in general, you should focus more on the structure of your code and apply unit-testing practices where it is most beneficial. The code coverage, the percentage of code covered by unit tests, is a reliable indicator of neither code-quality nor the bug-count. If you use orchestrators, the controller is so thin that it needs almost no testing. It makes sense to take as much code as possible out of the controller classes by using orchestrators, because it helps you to focus on use-cases in sufficient detail to give these orchestrators a programming interface. This is a great stimulus to write better code because you are forced to plan the code in terms of use-cases and design issues. By using orchestrators you reduce significantly the need to mock the HTTP context. If, for example, some session state must be consumed by the orchestrator, then the controller will access it, extract any data and pass it to the orchestrator. in most cases, you can focus on testing the orchestrator without being overly concerned with the HTTP context.

Testing Orchestrators

ASP.NET MVC does its best to support testing but it knows nothing about your application domain and the design you’ve come up with. ASP.NET MVC doesn’t write tests for you either. You should aim at writing tests that are relevant rather than aiming at getting a high score in code coverage. What’s the value in testing code like this?

The value of such a test is strictly related to the implementation of the method DoSomething. The method could be of any size; it might perhaps be executing a number of activities internally or might merely delegate the execution of the workflow to other layers. As I consider the controller to be part of the ASP.NET MVC infrastructure, I recommend the second approach. When the method DoSomething is just responsible for massaging some URL parameters and possibly some parts of the HTTP context into input values for orchestrators the value of testing DoSomething is much smaller than testing the orchestrator itself.

The orchestrator is a class that receives anything it needs to work from the outside, typically via dependency injection (DI), either coded manually or via an IoC container.

The orchestrator class has no dependencies on the HTTP context. instead, it usually has a few dependencies on such services as repositories and domain services. These dependencies are easily managed via DI and can be easily mocked when it comes to testing. in other words, testing is simplified and there is complete decoupling of framework and logic. As a developer, you focus on UI triggers and those tasks that need to be orchestrated in response. You simply use the controller as the built-in mechanism that matches UI requests to tasks. To put it another way, the controller is not part of your code.

When the Controller Orchestrates Itself…

Let’s review a scenario in which the controller does it all. You seem to have a simpler project structure and coordinate any actions related to the requested task from within the controller. Your DoSomething method may look like below:

The controller is instantiated by the registered controller factory. The default factory can only create controller instances by using their default constructor. To inject dependencies, therefore, you need to replace the factory. This is extra work.

in ASP.NET MVC 3 you can, perhaps, use dependency resolvers to save yourself the task of creating a factory. in terms of patterns, this means using a Service Locator based on IoC containers in order to inject dependencies. It works, and it seems elegant. I wouldn’t say this is simple though. It rather seems to me quite convoluted. With dependency resolvers, you’re writing less code yourself but you’re forcing the framework to do a lot more work.

Finally, by putting this logic in the controller, you are distracted from use-cases and overall design. Writing logic in the controller is exactly the same as writing the logic in Button1_Click. This is what a controller method actually is: a more elegant way of rendering a button click handler. Clean design, and with it effective and really quick testing, is still far away.

Final Thoughts

Personally, I have grown to love ASP.NET MVC. Mine was certainly not love at first sight. Love grew from appreciating the benefits of the neat separation between processing and rendering and the total control over the markup and responses. The care with which ASP.NET MVC was created was never extended to the tutorials. These, unfortunately, suggested to many developers that is was sufficient to merely use ASP.NET MVC in order to comply with best practices in architecture and design. It is certainly true, as the tutorials suggest, that testing is greatly simplified with ASP.NET MVC: Firstly, processing and rendering are neatly separated so you can intercept results being integrated in the view and write assertions on them. Secondly, some facilities exist to mock parts of a request that rely on the run time environment.

in ASP.NET MVC, smart tooling and framework magic make it possible for you to write code that can be unit-tested. Is it this also necessarily clean and well-designed code? I’d argue that ASP.NET MVC doesn’t guarantee you a good design. If you miss this point, you are likely to produce controllers whose design is just an updated version a of Button1_Click handler, and which are, in consequence, difficult to test.