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.