我试图了解如何对通过某种存储库访问数据的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;
}
}
答案 0 :(得分:3)
是的,你是对的,你把它传递给你的构造函数就像你拥有它一样。通过模拟IRepository
明确确保数据库相关代码不进入控制器进行测试,就像你想要的那样。
当您实际运行它时,您将需要设置应用程序以使用控件容器的反转来启用这些依赖项注入您的控制器(一些常用的应用程序是Ninject,{{3} }和StructureMap)。
以下是使用Windsor进行测试的示例:
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
以便按照自己的意愿行事。
答案 1 :(得分: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来说并不十分正确)
答案 2 :(得分:0)
我知道的最佳答案是使用Ioc容器: http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx
我更喜欢Castle Windsor
通过传入控制器依赖关系,您可以创建模拟。 我们有依赖项来实现可以模拟的接口。
答案 3 :(得分:0)
对于真实的,请查看nuget上的ninject mvc 3,对于单元测试,我更喜欢使用具有已知数据的内存中集合的虚假对象