Unit testing MVC 3:如何学习如何使用NUnit、Ninject和Moq进行测试?

Unit testing MVC 3:如何学习如何使用NUnit、Ninject和Moq进行测试?,unit-testing,asp.net-mvc-3,nunit,moq,ninject-2,Unit Testing,Asp.net Mvc 3,Nunit,Moq,Ninject 2,我的问题的简短版本: 有人能告诉我一些好的,详细的资料来源吗 可以学习如何在我的MVC 3应用程序中使用 努尼特、尼尼特2号和莫克 这里有人能帮我澄清一下控制器存储库是如何工作的吗 解耦、模拟和依赖注入一起工作 我的问题的较长版本: 我想做什么… 我目前正在开始创建一个MVC3应用程序,它将使用EntityFramework4,采用数据库优先的方法。我想把这件事做好,所以我正在尝试设计类、层等,使其具有高度的可测试性。但是,除了对单元测试或集成测试有学术上的理解之外,我几乎没有单元测试或集成测

我的问题的简短版本:

  • 有人能告诉我一些好的,详细的资料来源吗 可以学习如何在我的MVC 3应用程序中使用 努尼特、尼尼特2号和莫克
  • 这里有人能帮我澄清一下控制器存储库是如何工作的吗 解耦、模拟和依赖注入一起工作

  • 我的问题的较长版本:

    我想做什么…

    我目前正在开始创建一个MVC3应用程序,它将使用EntityFramework4,采用数据库优先的方法。我想把这件事做好,所以我正在尝试设计类、层等,使其具有高度的可测试性。但是,除了对单元测试或集成测试有学术上的理解之外,我几乎没有单元测试或集成测试的经验

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

    • NUnit作为我的测试框架
    • Ninject 2作为我的依赖注入框架
    • Moq作为我的模拟框架
    我知道哪个框架是最好的,等等,可以进入这个话题,但在这一点上,我真的没有足够的知识形成一个坚实的意见。所以,我决定采用这些免费的解决方案,这些方案似乎很受欢迎,而且维护得很好

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

    我花了一些时间研究这些内容,阅读了以下资源:

    从这些资源中,我已经设法解决了对存储库模式的需求,包括存储库接口,以便解耦控制器和数据访问逻辑。我已经在我的应用程序中写了一些,但我承认我不清楚整个事情的机制,也不清楚我是为了支持模仿还是依赖注入,或者两者兼而有之。因此,我当然也不介意听到你们的消息。在这一点上,我对这些东西的任何了解都会对我有所帮助

    事情对我来说变得一团糟……

    我以为我已经很好地掌握了这些东西,直到我开始尝试将我的头绕在Ninject上,如上文所述。具体地说,在作者开始描述服务层的实现时,我完全迷失了方向,大约在文档的一半

    无论如何,我现在正在寻找更多的资源来研究,以便尝试从不同的角度来看待这件事,直到它开始对我有意义为止

    总结所有这些,将其归结为具体问题,我想知道以下几点:

  • 有人能告诉我一些好的,详细的资料来源吗 可以学习如何在我的MVC 3应用程序中使用 努尼特、尼尼特2号和莫克
  • 这里有人能帮我澄清一下控制器存储库是如何工作的吗 解耦、模拟和依赖注入一起工作
  • 编辑:

    我刚刚在Github上发现了这个问题,所以我将开始研究它,看看它是否开始为我澄清问题。但是,我仍然对社区对这一切的想法非常感兴趣:)

  • 这是我正在创建的应用程序。它是开源的,可在github上使用,并利用了所有必需的东西-MVC3、NUnit、Moq、Ninject-

  • 控制库解耦很简单。所有数据操作都会移向存储库。存储库是某种IRepository类型的实现。控制器从不在自身内部创建存储库(使用
    new
    操作符),而是通过构造函数参数或属性接收存储库

  • 这种技术称为“控制反转”。为了支持控制反转,您必须提供一些“依赖注入”框架。Ninject是个不错的选择。在Ninject中,您将某些特定接口与实现类相关联:

    Bind<IUserRepository>().To<UserRepository>();
    
    当MVC基础结构即将创建新控制器时,调用被委托给定制控制器工厂GetControllerInstance方法,该方法将其委托给Ninject。Ninject发现,要创建该控制器,构造函数有一个类型为
    IUserRepository
    的参数。通过使用声明的绑定,它看到“我需要创建一个UserRepository来满足IUserRepository的需要。”它创建实例并将其传递给构造函数

    构造函数永远不知道将在内部传递什么样的实例。这完全取决于您为此提供的绑定

    代码示例:

    如果您使用的是nuget软件包,那么您链接的一些引起混淆的文章将不再需要。这个软件包有一切你需要开始注入你的控制器,这可能是最大的痛点

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

    private static void RegisterServices(IKernel kernel)  
    {  
      kernel.Bind<IRepository>().To<MyRepositoryImpl>();
      kernel.Bind<IWebData>().To<MyWebDAtaImpl>();
    }        
    
    如果您正在进行非常高的测试覆盖率,那么基本上只要一个逻辑代码段(比如控制器)需要与另一个逻辑代码段(比如数据库)对话,您就应该创建一个接口和实现,将定义绑定添加到RegisterService,并添加一个新的构造函数参数

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

    然后,当涉及到测试时,您要做的是提供所有必需接口的模拟版本,这样您要测试的唯一内容就是编写测试的方法中的代码。在我的例子中,我假设
    public class TrackyControllerFactory : DefaultControllerFactory
    {
        private IKernel _kernel = new StandardKernel(new TrackyServices());
    
        protected override IController GetControllerInstance(
            System.Web.Routing.RequestContext requestContext,
            Type controllerType)
        {
            if (controllerType == null)
            {
                return null;
            }
    
            return _kernel.Get(controllerType) as IController;
        }
    }
    
    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;
        }
    }
    
    bool TryCreateUser(string username);
    
    public ActionResult CreateUser(string username) {
        if (_Repo.TryCreateUser(username))
           return RedirectToAction("CreatedUser");
        else
           return RedirectToAction("Error");
    }
    
    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"]);
    }