我的ASP.NET MVC控制器应该如何识别存储库

问题描述 投票:5回答:4

我试图了解如何对通过某种存储库访问数据的ASP.NET MVC项目进行单元测试。

在单元测试期间,我显然想要创建一个模拟存储库但是如何将这个模拟存储库传递给正在测试的Controller实例?另外,真正连接到数据库的实际存储库如何找到控制器的路径?

我是否只是通过构造函数执行此操作,如下所示?我认为这是我应该如何设置我的控制器,但我想确认这是正确的:

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;
    }
}
asp.net-mvc unit-testing dependency-injection repository-pattern
4个回答
3
投票

正如大家已经说过的那样,你会想要使用IoC *或DI **容器。但他们没有说的是为什么会这样。

我们的想法是,DI容器将允许您绕过ASP.NET MVC的默认控制器构造策略,即需要无参数构造函数。因此,您可以让控制器明确说明其依赖关系(最好是接口)。这些接口如何映射到具体实例是DI容器的业务,您可以在Global.asax.cs(实时)或测试夹具设置(用于单元测试)中配置。

这意味着您的控制器不需要了解其依赖项的具体实现,因此我们遵循依赖性倒置原则:“高级模块不应该依赖于低级模块。两者都应该依赖于抽象。”

例如,如果您要使用AutoFac,则可以执行以下操作:

// 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 =控制反转 ** DI =依赖倒置 (这两个术语经常互换使用,这不是真正正确的IMO)


3
投票

是的,你是对的,你把它传递给你的构造函数就像你拥有它一样。通过模拟IRepository,您明确地确保数据库相关代码不会像您想要的那样进入控制器进行测试。

实际运行它时,您需要设置应用程序以使用控件容器的反转,以便将这些依赖项注入控制器(一些流行的应用程序是NinjectStructureMapWindsor)。

以下是使用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);
}

现在,您可以测试您的行为并模拟您的IRepository以表现您想要的行为。


0
投票

我知道的最佳答案是使用Ioc容器:http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

我更喜欢Castle Windsor

通过传入控制器依赖关系,您可以创建模拟。我们有依赖项来实现可以模拟的接口。


0
投票

对于真实的,请查看nuget上的ninject mvc 3,对于单元测试,我更喜欢使用带有已知数据的内存中集合的虚假对象

© www.soinside.com 2019 - 2024. All rights reserved.