开发者

How should my ASP.NET MVC Controllers be aware of the repository

I'开发者_StackOverflowm trying to get my head around how one would unit test an ASP.NET MVC project that accesses data through a repository of some sort.

During the unit tests I'd obviously want to create a mock repository but how do I pass this mock repository to the Controller instance being tested? Also how would the actual repository, that's really connected to a database, find its way to the controller?

Do I simply do this through the constructors as I've shown below? I think this is how I should set up my controllers, but I'd like some confirmation that this is correct:

public class SampleController : Controller
{
    private IRepository _repo;

    //Default constructor uses a real repository
    // new ConcreteRepo() could also be replaced by some static 
    // GetRepository() method somewhere so it would be easy to modify
    //which concrete IRepository is being used
    public SampleController():this(new ConcreteRepo())
    {

    }

    //Unit tests pass in mock repository here
    public SampleController(IRepository repo)
    {
        _repo = repo;
    }
}


As everyone has already said, you'll want to use an IoC* or DI** container. But what they haven't said is why this is the case.

The idea is that a DI container will let you bypass ASP.NET MVC's default controller-construction strategy of requiring a parameterless constructor. Thus, you can have your controllers explicitly state their dependencies (as interfaces preferably). How those interfaces map to concrete instances is then the business of the DI container, and is something you will configure in either Global.asax.cs (live) or your test fixture setup (for unit testing).

This means your controller doesn't need to know anything about concrete implementations of its dependencies, and thus we follow the Dependency Inversion Principle: "High-level modules should not depend on low-level modules. Both should depend on abstractions."

For example, if you were to use AutoFac, you would do this:

// In Global.asax.cs's Application_Start
using Autofac;
using Autofac.Integration.Mvc;

var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.Register<IRepository>(() => new ConcreteRepo());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

// In your unit test:
var controllerInstance = new SampleController(new InMemoryFakeRepo());

// In SampleController
public class SampleController : Controller
{
    private readonly IRepository _repo;

    public SampleController(IRepository repo)
    {
        _repo = repo;
    }

    // No parameterless constructor! This is good; no accidents waiting to happen!
    // No dependency on any particular concrete repo! Excellent!
}

* IoC = inversion of control
** DI = dependency inversion
(the two terms are often used interchangeably, which is not really correct IMO)


Yeah, you're correct, you pass it to your constructor like you have it. By mocking IRepository your explicitly ensuring that the database dependent code doesn't get into the controller for testing, like you want.

When you actually run it, you'll want to setup your application to work with an inversion of control container to enable those dependencies to be injected into your controller (some popular ones are Ninject, StructureMap, and Windsor).

Here's a sample of testing using Moq:

private Mock<IRepository> _mockRepo;
private SampleController _controller;

[TestInit]
public void InitTest()
{
    _mockRepo = new Mock<IRepository>();
    _controller = new SampleController(_mockRepo.Object);
}

[Test]
public void Some_test()
{
    _mockRepo.Setup(mr => mr.SomeRepoCall()).Returns(new ValidObject());

    var result = _controller.SomeAction() as ViewResult;
    Assert.IsNotNull(result);
}

Now you can test your actions and mock your IRepository to behave however you want.


The best answer I know is to use an Ioc Container: http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

I prefer Castle Windsor

With the controller dependencies passed in you can then create mocks. We have dependencies that implement interfaces which can be mocked.


For the real one, check out ninject mvc 3 on nuget, for unit testing I prefer to use fake objects with in-memory collections of known data

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜