We now have the basics in place for our domain model. We have two options to proceed, we can develop the controller or we can flush out the remainder of the persistence wiring along with the mechanism to retrieve results from the database. As a general rule, there are two things that we want to delay as long as possible: writing the presentation layer and writing any data access code against a live database. Delaying these keeps our attention focused on the domain and the interactions with the behavior of the domain. (But what about those breaking data tests? In my daily work, I’d put in
just enough persistence wiring to get the data tests passing again, but I’d rather not jump back and forth between the logic and data layers for this tutorial. Once you become comfortable with the minimal persistence specific wiring, discussed later, you’ll start putting it in simply out of habit and you won’t be hiding the "DB Tests" category for later.) Therefore, let’s proceed with developing the controller and we’ll fake our interactions with a live database.
- As always, let’s start with a unit test. Under Northwind.Tests/Northwind.Web/Controllers, add a new class called StaffMembersControllerTests which contains a single test. An explanation of what is happening follows the code.
using NUnit.Framework;
using Northwind.Core;
using MvcContrib.TestHelper;
using SharpArch.Core.PersistenceSupport;
using Rhino.Mocks;
using System.Collections.Generic;
using NUnit.Framework.SyntaxHelpers; // remove for NUnit 2.5+
using Northwind.Web.Controllers;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
namespace Tests.Northwind.Web.Controllers
{
[TestFixture]
public class StaffMembersControllerTests
{
[Test]
public void CanListFilteredStaffMembers() {
StaffMembersController controller =
new StaffMembersController(
CreateMockStaffMemberRepository());
ViewResult result =
controller.ListStaffMembersMatching("martin")
.AssertViewRendered()
.ForView("ListStaffMembersMatchingFilter");
Assert.That(result.ViewData, Is.Not.Null);
Assert.That(result.ViewData.Model as List<StaffMember>, Is.Not.Null);
Assert.That((result.ViewData.Model as List<StaffMember>).Count, Is.EqualTo(4));
}
/// <summary>
/// In most cases, we'd simply return
/// IRepository<MyEntity>, but since we're
/// leveraging a custom Repository method, we need a
/// custom Repository interface.
/// </summary>
public IStaffMemberRepository CreateMockStaffMemberRepository() {
MockRepository mocks = new MockRepository();
IStaffMemberRepository mockedRepository = mocks.StrictMock<IStaffMemberRepository>();
Expect.Call(mockedRepository.FindAllMatching(null))
.IgnoreArguments()
.Return(CreateStaffMembers());
mocks.Replay(mockedRepository);
return mockedRepository;
}
private List<StaffMember> CreateStaffMembers() {
List<StaffMember> staffMembers =
new List<StaffMember>();
staffMembers.Add(new StaffMember("ABC123"));
staffMembers.Add(new StaffMember("DEF456"));
staffMembers.Add(new StaffMember("GHI789"));
staffMembers.Add(new StaffMember("Abracadabera"));
return staffMembers;
}
}
}
That’s quite a mouthful! Here’s what the code is trying to verify… It’s making sure that if we call the controller’s ListStaffMembersMatching() method, then the controller will invoke the IStaffMemberRepository.FindAllMatching() method to retrieve the results and put them into the ViewData.
Notice that the constructor of the controller accepts the return value from CreateMockStaffMemberRepository(), which is a concrete instance of IStaffMemberRepository. This doesn’t exist yet, but we know that the controller is going to need it…this is the first D in TDD. By passing IStaffMemberRepository into the constructor of the controller, we’re performing “manual dependency injection,” enabling ourselves to unit test the controller without a live database. For more information on dependency injection, read http://www.codeproject.com/KB/architecture/DependencyInjection.aspx - the “mock” objects in this article are actually “stubs”…but I digress.
The CreateMockStaffMemberRepository() method itself is an example of creating a true mock object, using an external library called Rhino Mocks (http://www.ayende.com/projects/rhino-mocks.aspx). This mock object simulates interaction with a live database. In this way, we’ve foregone digging into the data access layer until we have the business logic completely pinned down...no, it’s not because we’re hopeless procrastinators putting off all the tedious work until the interns start, we simply want to be able to focus on the domain logic until the details are worked out. For more information on mocking the database (no pun intended), see http://www.martinfowler.com/articles/mocksArentStubs.html.
(While we’re talking about testing the controller, if you feel limited in ASP.NET MVC’s ability to support robust unit testing of controllers in an HTTP context, take a look at the MvcContrib project at http://www.codeplex.com/MVCContrib for additional capabilities; the assemblies for it are already included with S#arp Architecture.)
- Compile the solution and see the compilation break. It’ll complain because neither StaffMembersController nor IStaffMemberRepository exist yet.
- To get the code to compile, create IStaffMemberRepository first since it’s a dependency of the controller. To do so, go to Northwind.Core/DataInterfaces and add a new interface called IStaffMemberRepository, as follows:
using SharpArch.Core.PersistenceSupport;
using System.Collections.Generic;
using Northwind.Core;
namespace Northwind.Core.DataInterfaces
{
public interface IStaffMemberRepository : IRepository<StaffMember>
{
List<StaffMember> FindAllMatching(string filter);
}
}
As shown, this inherits from IRepository<> which resides in SharpArch.Core. This general-use Repository interface declares a number of standard Repository methods such methods as GetAll() and Delete(). All that needs to be done for now is to extend it with one additional, custom method, FindAllMatching().
When using S#arp Architecture, IRepository<MyObject> should be used as the basis for all custom repositories. If your object has an ID type other than int, even a composite type, then implement IRepositoryWithTypedId<MyObject, IdType>, instead. (Incidentally, IRepository<MyObject> inherits from IRepositoryWithTypedId<MyObject, int>.)
The key point is that the Northwind.Core assembly does not contain any data access implementation details; it only declares, and has access to, data access interfaces. As discussed later on, the concrete Repository, containing the implementation details, will be added to Northwind.Data project (but not until we need it).
Having the interface separated from the implementation details located in a different assembly is known, appropriately enough, as “Separated Interface” (http://www.martinfowler.com/eaaCatalog/separatedInterface.htm). (Uncle Bob, aka Robert Martin, originally coined this technique as “Dependency Inversion.”) For more information concerning refactoring towards Separated Interface, see http://devlicio.us/blogs/billy_mccafferty/archive/2008/10/30/refactoring-service-dependencies-to-separated-interface.aspx.
Getting back to the task at hand, go to the Northwind.Web.Controllers project and add a StaffMembersController class; it should have just enough code to compile:
using Northwind.Core;
using System.Collections.Generic;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
using SharpArch.Web;
namespace Northwind.Web.Controllers
{
public class StaffMembersController : Controller
{
public StaffMembersController(IStaffMemberRepository staffMemberRepository) {}
public ActionResult ListStaffMembersMatching(
string filter) {
return null;
}
}
}
- Build the solution, head back to NUnit, run the CanListFilteredStaffMembers test, and see it fail it a fit of agonizing pain due to an object reference exception. Son of a!!! (But wait, that’s exactly what we wanted to see at this point.)
- We now simply have to add a couple lines of code to the controller to get the unit test to pass:
using Northwind.Core;
using System.Collections.Generic;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
using SharpArch.Core;
using SharpArch.Web;
namespace Northwind.Web.Controllers
{
public class StaffMembersController : Controller
{
public StaffMembersController(IStaffMemberRepository staffMemberRepository) {
Check.Require(staffMemberRepository != null,"staffMemberRepository may not be null");
this.staffMemberRepository = staffMemberRepository;
}
public ActionResult ListStaffMembersMatching(string filter) {
List<StaffMember> matchingStaffMembers = staffMemberRepository.FindAllMatching(filter);
return View("ListStaffMembersMatchingFilter", matchingStaffMembers);
}
private readonly IStaffMemberRepository staffMemberRepository;
}
}
Arguably, adding the Check.Require within the constructor is a little more than “just enough” code to get the test to pass. But with that said, adding Check.Require statements all over the place should become habitual while coding. It only takes a moment to write and will end up saving you hours in the debugger. And if you’re worried about these checks causing a performance hit, there are far larger fish to fry than these.
-
Now run the NUnit test again and you should be seeing green. We were able to do all of this without yet defining our staff members table in the database. But we’re not going to get much further without doing that as well…