单元测试 – MVC 3:如何学习如何使用NUnit,Ninject和Moq进行测试?

我的问题的简短版本:

>任何人都可以指点我一些好的,详细的来源,我从中
可以学习如何在我的MVC 3应用程序中实现测试,使用
NUnit,Ninject 2和Moq?
>任何人都可以帮助澄清我如何控制器存储库
解耦,模拟和依赖注入一起工作?

我的问题较长版本:

我想做什么…

我目前开始创建一个MVC 3应用程序,它将使用Entity Framework 4,用数据库第一种方法。我想做到这一点,所以我试图设计类,层等,以高度可测试。但是,我有很少或没有单元测试或集成测试的经验,除了对他们的学术理解。

经过大量的研究,我决定使用

> NUnit作为我的测试框架
> Ninject 2作为我的依赖注入框架
> Moq作为我的模拟框架。

我知道哪个框架是最好的话题,等等,可以进入这一点,但在这一点上,我真的不知道任何它形成一个坚定的意见。所以,我只是决定去与这些自由的解决方案,似乎很喜欢和良好的维护。

我到目前为止学到了什么…

我花了一些时间通过这些东西,阅读资源,如:

> Implementing the Repository and Unit of Work Patterns in an
ASP.NET MVC Application

> Building Testable ASP.NET MVC Applications
> NerdDinner Step 12: Unit Testing
> Using Repository and Unit of Work patterns with Entity Framework
4.0

从这些资源,我设法了解一个Repository模式的需要,完成与存储库接口,以解耦我的控制器和我的数据访问逻辑。我已经写了一些到我的应用程序已经,但我承认我不清楚整个事情的机制,以及我是做这种解耦支持嘲笑或依赖注入,或两者。因此,我当然不介意听到你们这方面的。我可以从这个东西获得任何清晰度将帮助我在这一点上。

那里的东西对我来说是泥泞的…

我以为我是抓住这东西很好,直到我开始包围我的头围绕Ninject,如Building Testable ASP.NET MVC Applications,上面引用的描述。具体来说,我完全失去了作者开始描述一个服务层的实现,大约在文档的一半。

无论如何,我现在正在寻找更多的资源来研究,以试图获得各种观点围绕这个东西,直到它开始对我有意义。

总结所有这一切,沸腾它的具体问题,我想知道以下:

>任何人都可以指点我一些好的,详细的来源,我从中
可以学习如何在我的MVC 3应用程序中实现测试,使用
NUnit,Ninject 2和Moq?
>任何人都可以帮助澄清我如何控制器存储库
解耦,模拟和依赖注入一起工作?

编辑:

我刚刚在Github上发现了Ninject official wiki,所以我将开始工作,看看它是否开始澄清我的东西。但是,我仍然很感兴趣的SO社区的想法,所有这一切:)

最佳答案
如果你使用Ninject.MVC3 nuget包,那么你链接的一些文章,导致混乱将不是必需的。该包具有一切你需要开始注入你的控制器,这可能是最大的痛点。

在安装该软件包时,它将在App_Start文件夹中创建一个NinjectMVC3.cs文件,在该类内部是一个RegisterServices方法。这是你应该在你的接口和你的实现之间创建绑定的地方

private static void RegisterServices(IKernel kernel)  
{  
  kernel.Bind<IRepository>().To<MyRepositoryImpl>();
  kernel.Bind<IWebData>().To<MyWebDAtaImpl>();
}        

现在在你的控制器中,你可以使用构造函数注入。

public class HomeController : Controller {  
    private readonly IRepository _Repo;
    private readonly IWebData _WebData;

    public HomeController(IRepository repo, IWebData webData) {
      _Repo = repo;
      _WebData = webData;
    }
}

如果你是在非常高的测试覆盖率之后,那么基本上任何时候一个逻辑代码段(说控制器)需要与另一个(说数据库)谈话你应该创建一个接口和实现,添加定义绑定到RegisterService并添加一个新的构造函数参数。

这不仅适用于Controller,而且适用于任何类,因此在上面的示例中,如果您的存储库实现需要WebData的某个实例,则可以将readonly字段和构造函数添加到您的存储库实现中。

然后,当涉及到测试,你想做的是提供所有需要的接口的模拟版本,所以你唯一的测试是你正在编写测试的方法中的代码。所以在我的例子中,说IRepository有一个

bool TryCreateUser(string username);

其中由控制器方法调用

public ActionResult CreateUser(string username) {
    if (_Repo.TryCreateUser(username))
       return RedirectToAction("CreatedUser");
    else
       return RedirectToAction("Error");
}

你真正试图在这里测试是if语句和返回类型,你不想创建一个真正的存储库,将返回true或false基于特殊值你给它。这是你想嘲笑的地方。

public void TestCreateUserSucceeds() {
    var repo = new Mock<IRepository>();
    repo.Setup(d=> d.TryCreateUser(It.IsAny<string>())).Returns(true);
    var controller = new HomeController(repo);
    var result = controller.CreateUser("test");
    Assert.IsNotNull(result);
    Assert.IsOfType<RedirectToActionResult>(result)
    Assert.AreEqual("CreatedUser", ((RedirectToActionResult)result).RouteData["Action"]);
}

^这不会为你编译,因为我知道xUnit更好,并不记得在RedirectToActionResult从我的头顶部的属性名称。

所以总结一下,如果你想要一块代码与另一块代码交谈,在它们之间打一个接口。然后,这允许你模拟第二段代码,以便当你测试第一个,你可以控制输出,并确保你只测试有问题的代码。
我认为这就是这一点真的使我的一分钱,所有这一切,这不一定是因为代码需要它,但因为测试需要它。

最后一个建议专门针对MVC,任何时候你需要访问基本的Web对象,HttpContext,HttpRequest等,包装所有这些背后的接口(像我的示例中的IWebData),因为虽然你可以模拟这些使用*基类,它变得很痛苦非常快,因为他们有很多内部依赖,你也需要模拟。
还有Moq,设置MockBehaviour在创建mock时严格,它会告诉你,如果有什么被调用,你没有提供一个模拟。

转载注明原文:单元测试 – MVC 3:如何学习如何使用NUnit,Ninject和Moq进行测试? - 代码日志