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

Eri*_*tas 5 asp.net-mvc unit-testing dependency-injection repository-pattern

我试图了解如何对通过某种存储库访问数据的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Dom*_*nic 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!
}
Run Code Online (Sandbox Code Playgroud)

* IoC = 控制反转
** DI = 依赖反转
(这两个术语经常互换使用,在我看来这并不正确)