Unit testing per se is a controversial topic, what you should unit test is another controversial topic. The topic of this post started by a short conversation during a .NET London Group meeting back in 2010, I thought about it and then gave it more thoughts, I started using it in my projects, then now I have enough confidence to share my thoughts. Here is my opinion, if you agree or disagree, then please do comment.
Controllers should be slim
You’ve heard this before: “Controllers shouldn’t be fat, controllers should contain minimal code”, it’s a mantra.
The S in SOLID stands for SRP. A controller should have one role, it is the communication layer between a model and a view. Even with this role, it is violating SRP, one could say maybe we need two classes to take the role of a controller or a controller per action method, but let’s not delve into that.
The controller, by role, is overloaded with responsibilities, adding more responsibilities to it is a design crime (maybe one should be penalised by losing some StackOverflow or CodeProject points ).
Why do controllers get fat?
I did blog earlier on how views become fat Three Steps to Get Fat-Razor MVC Views on Diet, some points are common and some are specific to controllers:
Explicit validation of data in the action method
Validation of your viewmodel data in your controller violates two principles:
- SRP Design Principle: You are attaching more responsibilities to the controller.
- Encapsulation OOP Principle: You will have to expose your data outside your viewmodel to allow another object, the controller, to validate it, while the object itself should be responsible for its own data.
ASP.NET MVC 4 has a good validation ecosystem that will help you moving your validation code to where it belongs.
Mapping a model to a viewmodel
The mapping from a model to a viewmodel should not be done in the action method. My favourite solution is to use a CQRS type query pattern so I don’t have to do this boilerplate anywhere. Here is a good Introduction to CQRS.
Leaking some business logic
This is not a place for any business logic. The controller should be calling a service, of a classical DDD implementation, or issuing a query or a command of CQRS.
Http-related code in the action method
Having a special HTTP case that requires special handling should not be done here. Action filters might be the answer.
What are you unit testing?!
Given the above and if your action methods have almost no code, then what are you unit testing? Do you want to test if you are returning the proper view or do you want to test if you have decorated your action method with the proper attributes? I wouldn’t, I usually have a deadline to hit and I am against boilerplate code.
If someone tells me their controller requires unit testing, I will ask, did you design your controller properly?
Unit testing is a mean for providing higher quality software and not an aim per se. Having too many redundant unit tests will cause a future maintenance headache and will cost more project development time.
If an area/layer requires unit testing then you should probably test it, but for other areas more tests may be less quality.
Update - I told you not to unit test your controllers, by keeping them empty, here is one way to keep your controllers thin: Implementing a CQRS-based Architecture with MVC and Document DB.