Testing Controllers in ASP.NET MVC
page 3 of 5
by Manning Publications
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 24189/ 52

Making Dependencies Explicit

You probably notice that there are only three real lines of code in the RedirectController. The controllers should all be quite thin, and this is a good example of that. The logic for finding the correct Conference object does not belong in the controller, so it is factored into a repository object. The only logic that belongs is the logic related to presenting information to the user. In this case, the user experiences a redirect. This controller demonstrates proper separation of concerns, and it is easily unit tested because it is only concerned with a single responsibility. We are able to simulate dependencies using test doubles.

Figure 1 Redirect test passing

In Figure 1, you see the unit test passing because we were able to properly simulate this controller’s dependencies and verify that given the dependencies, this controller will do its job correctly.

Using Test Doubles, Such as Stubs and Mocks

As far as the controller is concerned, its caller is passing in an implementation of the necessary interface. This interface is a dependency, and the controller will make use of it in an action method. How the dependency is passed in or what class implements the interface is irrelevant. At runtime, a production class will be passed into the controller, but at the time of unit testing, we use stand-in objects, or test doubles, to simulate the behavior of the dependencies. There are different types of simulated objects, and some of the meanings overlap. There are entire books written about testing and how to separate code for testing using fakes, stub, and mocks, and if you are interested in exploring the subject further, we would highly recommend reading Michael Feather’s book, Working Effectively with Legacy Code. In short, a fake, or test double, is a pretty generic term used to mean a non-production implementation of an interface or derived class serving the purpose of a stand-in for the real thing. Stubs are classes that return hard-code information just for the purpose of being called. The ControllerRepositoryStub shown in listing 1 is an example of a stub. A mock is a recorder. It will remember being called so that we can assert the behavior later on. It will remember arguments passed in and other details depending on what capability has been programmed into it.

A downside to using coded stubs and mocks is that you have many lines of code just to satisfy an interface implementation that may have six methods. This is not the only option, however. A favorite library for automating the creation of mocks and stubs is Rhino Mocks, originally written by Oren Eini. Rhino Mocks drastically reduces the number of lines of code in a unit test fixture by streamlining the creating of test doubles. If code is designed so that all dependencies are injected into the constructor, as shown in listing 2, unit testing because very easy to do and soon becomes a repetitive pattern of faking our dependencies and writing assertions. Over time, if you employ this technique, you will see a marked improvement in the quality of your code.

Listing 2 – Controllers can define dependencies in the constructor

public RedirectController(IConferenceRepository conferenceRepository) 
{ 
      _repository = conferenceRepository; 
}

For just a minute remember back to how many line of code we wrote for a stubbed implementation of IConferenceRepository. Now, examine listing 3 and notice how short this code listing is compared to the previous ones. Rhino Mocks supports setting up dynamic stub as well as dynamic mocks. The lines with “Stub(…)” are used for stubbing method or property to always return a given object. By using the Rhino Mocks library, we are able to provide dependency simulations quickly for easy unit testing.

Listing 3 – Using Rhino Mocks to streamline code necessary for fakes

using System.Web.Mvc; 
using CodeCampServer.Core.Domain; 
using CodeCampServer.Core.Domain.Model; 
using NUnit.Framework; 
using NUnit.Framework.SyntaxHelpers; 
using Rhino.Mocks; 
 
namespace MvcInAction.Controllers.UnitTests 
{ 
      [TestFixture] 
      public class RedirectControllerTesterWithRhino 
      {
            [Test]
            public void ShouldRedirectToTheNextConference()
            {
                  //arrange
                  var conferenceToFind = 
                        new Conference {Key = "thekey", Name = "name"};
                  // create a stubbed implementation of the interface using
                  // Rhino Mocks
                  var repository =                                
                        MockRepository.GenerateStub<IConferenceRepository>();
                  //intruct the stub to return a specific conference when called
                  repository.Stub
                        (r => r.GetNextConference()).Return(conferenceToFind);
 
                  //act
                  var controller = new RedirectController(repository);
                  RedirectToRouteResult result = controller.NextConference();
 
                  //assert - ensure the operation did everything expected
                  Assert.That(result.RouteValues["controller"],
                        Is.EqualTo("conference"));
                  Assert.That(result.RouteValues["action"], Is.EqualTo("index"));
                  Assert.That(result.RouteValues["conferenceKey"],
                        Is.EqualTo("thekey"));
            } 
      } 
}

A dynamic mocking library like Rhino Mocks is not appropriate in every unit testing scenario. The usage in listing 3 is the bread-and-butter scenario that saves a lot of setup code inside unit tests. More complex needs can quickly stress the Rhino Mocks API and become hard to read. While Rhino Mocks does have support for almost everything you could want to do, readability of the tests is important to maintain. When you need to assert method parameters of dependencies or do something special, do not be afraid to push Rhino Mocks to the side and leverage a concrete mock or stub in order to keep the test readable.


View Entire Article

User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-10-14 8:15:29 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search