Covering MVC.NET loosely-coupled application with unit tests using Rhino.Mock and MvcContrib.TestHelper
In this post I am going to review how we can test our MVC.Net application layers in loosely-coupled application. In my last article I wrote about IoC with MVC.Net, so I will use this code as a baseline and will try to cover it with unit tests. Despite MVC.Net implies separation of concerns it doesn’t mean that such application is easy testable by default. Yet IoC helps us to define smaller isolated chunks which are quite more testable. When we talk about testing I have to say that few users click and running through the application for making sure that most visible errors are removed is not a testing…period. Unfortunately in lot of software development teams the testing is exactly what I pointed – going through product’s functionality and verifying that all visible (the most obvious) errors are fixed. In my opinion this approach may work in very limited scenarios, mostly for very small non complex applications. Everything beyond this “definition” is doomed to failure if you don’t go for automation testing. Today’s topic is about one part of the automation testing – unit tests. Unit-testing should aim at covering all of the application logic (or as much as possible :), something is definitely better than nothing). Every single method should be executed at least once to ensure that all code works correctly. When unit-testing, we should always test the smallest piece of testable software and we should able to run the tests continuously and in isolation from the “real” code and each other.
The major benefits of unit testing are facilitating the development process and the fact that they are kind of best documentation (from development perspective) for our logic’s implementation. During the development we either re-factor our code or we change it. If we have good unit-tests behind, it makes easier to figure out a deviation from the expected behavior during the changes.
What we have as isolated pieces in my PoC from previous post is: Business layer (Services), Data access layer (Repositories) and the MVC.Net layers (Models, Views, Controllers). In the PoC I have created I don’t use the model and in the real world my services should be a top layer addition to the domain logic based on the models, but it really doesn’t matter for the purposes of this article.
I want to test my code in isolation and here comes the Rhino.Mock mocking framework. As it comes to MvcContribl.TestHelper, it provides unit testing on MVC.Net controllers through mocking their internal members like Request, Response and Session. That makes it quite handy because without having all this out-of-the-box we should implement our mocks which in some cases may be a lot of work.
We have next dependencies which we’d like to mock in order testing in isolation:
• Controllers depends on the services which are injected with Spring.Net
• Services depends on repositories which are injected with Spring.Net
• Repositories depends on the database context which is injected with Spring.Net
Before I start reviewing these aspects I’d like to mention that I’ve spent the time to port my tests for both Nunit and MSTests. There are slight differences between their attributes and some assertions which technically can be overcome by using preprocessor directives which makes the same tests portable for both Visual studio and Nunit.
It might be something like that:
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
Find below the difference in attributes between MS Test and Unit I used:
MS Test: TestClass -> NUnit: TestFixture
MS Test: TestMethod -> NUnit: Test
MS Test: ClassCleanup -> NUnit: TearDown
MS Test: ClassInitialize -> NUnit: SetUp
Now let’s see how we can achieve our goals for all this isolated parts of our application.
1. Repositories testing
Well, honestly mocking (isolating) the Linq to Entities data context in the context of its implementation in my baseline code was pain in the butt. Spring.Net injects the instance of AuthenticationDemoEntities in every repository. If I want to isolate it I have to create either brand new implementation which should be successor of the “real” database context or to create mock object wich should be able to replace runtime the “real” implementation. My IDBContext helped a lot here. Till now it was only a marker (bad practice LOL), but I added some contracts and did their implementations in the partial class of Linq To Entities context. Doing so I kept the code independent from generated code by Linq To Entities and in the same time I fed my contract properties by the “real” database model objects which made mocking possible. Of course I touched a bit repositories implementations, but it was a necessary change and affected few lines. So not a biggy.
Below are some screens which will give you better understanding of my idea. At the bottom of this article you can find a link to download article’s sample.
Database context objects feed my IEnumarable collections. RemoveObject and AddNewObject call the appropriate object context methods for manipulation of the data.
In the repositories classes now we use the explicit implementation of our contracts:
Having all this done, we can do the mocking and unit testing our data access layer in isolation.
Here is sample of testing a repository method by mocking Linq to Entities database context:
What I didn’t mention is my collection manager which simply cares about maintaining a dummy objects (which replaces the results returned by invocations against the database). I used Rhino mock for mocking my database contexts and recording mocks expectations.
Well, now we can test our repositories without touching the database. You can find repositories’ tests methods in RepositoriesTests folder in tests projects.
2. Services testing
When it comes to the services they rely on repositories implementations which are injected at run-time by Spring.Net.
The only thing we need to do in order to handle this dependency is mocking repositories which are used in services’ implementations.
You can find services’ tests methods in ServicesTests folder in tests projects.
3. Controllers testing
When it comes to unit-testing controllers in one MVC.Net application the helper frameworks may vary depending on controller’s implementations. If we use redirections, request, response and session objects MvcContrib.TestHelper is quite handy and gives many assertion methods. We can still disregard it and implement our mocks, but when it comes to the mentioned scenarios it may take a long time and development efforts for getting the result which can be achieved with few lines of code.
My code is very poor when it comes to the controllers’ implementations, so it didn’t really imply to using MvcContrib.TestHelper.
In my controllers’ test methods I went farther than just checking that action result is not null. Since I am using json result I wanted to test its properties too. For this purpose I serialized and de-serialized the result and made assertions against its properties depending on the tested scenario.
You can find controllers’ tests methods in ControllersTests folder in tests projects.
4. MVC routing
I did cover the MVC’s routing with test just to demonstrate how easy is to do this using MvcContrib.TestHelper. I don’t have any routes different from default one. In the RoutingTests.cs there are samples demonstrating it by using both mock and test helper library. And it is obvious that test helper library rocks here.
Consideration and projects dependencies:
Here is a list of dlls and projects dependencies which should be referred in our test projects:
• System.Runtime.Serialization – for seriazliing and de-serializing json results
• System.ServiceModel.Web - for seriazliing and de-serializing json results
• System.Web.Extensions - for seriazliing and de-serializing json results
• nunit.framework.dll – for running our tests with Nunit
• Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll – for running our tests with MS Test
All external libraries can be found in External-Tests folder.
Beside the 3rd party libraries and system libraries we need next project reference: (technically every library we cover with unit-tests should be reffered)
• Definitions – contains interfaces contracts
• EntitiesDefinitions - contains interfaces contracts
• DataAccessLayer – contains the implementation of the repositories
• Admin – contains the implementation of services
• DataAccess – should be referred only because it is used for mocking data context.
• MVCAuthenticationSample – our MVC.Net application, which we refer to test routes
Debugging is quite easy with both MS Test and NUnit. To debug a test which is run with NUnit just put a breakpoint and attach to the NUnit process. Another feature that might be helpful is the code coverage tool. If you have the right edition of Visual Studio, which supports code coverage it makes sense to write your unit tests for VS. A good alternative (as none license-free can be called good) is NCover. Still the code coverage in my understanding can be both very powerful control mechanism and in the same time a bit misleading, so it should not be a measure for the quality of your code. We should agree that it is easy to generate unit tests with no assertions and we will have our code coverage despite it won’t bring any value.
Let’s see how our unit tests will alert us when there is a deviation from the code’s expectation.
Let’s say someone comments out one of ours ignore route rules and referring Scripts is not an ignore rule any longer.
If we run our unit tests we will get a failure instantly:
Now we have isolated tests which can be run independently and continuously.
You can refer my new article for designing loosely coupled MVC.NET 3 applications here.
Article related links:
MvcContrib MVC 1.1 containing MvcContrib.TestHelper
You can download this article’s source code from here.
Database backup can be found in the repository for my previous article about IoC.
To start the tests with Visual studio create new test lists and add the unit tests.