为MVC模块创建一个单元测试

概述

由于MVC控制器是MVC模块的中心业务逻辑,因此创建自动化单元测试以确保它们的行为符合预期是一种最佳实践。这个例子说明了如何为模块的MVC控制器创建一个单元测试。

注意:此单元测试过程仅适用于使用DNN MVC模块模板创建的模块。

先决条件

  • 在Visual Studio中使用DNN MVC模块模板作为带模板的项目创建的模块。
  • Moq,一个c# /. net仿真框架。

步骤

  1. 向MVC模块解决方案添加一个新的单元测试项目。
    1. 在Visual Studio的解决方案资源管理器,右键单击MVC模块解决方案并选择添加>新项目

      添加新项目

    2. 添加新项目对话框中,选择单元测试项目,输入名称,并选择待保存的本地文件夹。

      创建单元测试项目

  2. 添加必要的MVC和DNN程序集引用。

    对于要添加到新单元测试项目中的每个程序集,右键单击项目的参考文献节点并添加程序集引用。


    项目参考

    添加对以下程序集的引用,以及模块特别需要的其他程序集:

    • DotNetNuke
    • DotNetNuke.Web.Mvc
    • System.Web.Mvc
  3. (可选)使用Moq模拟一个数据存储。

    Moq是c# /的仿真框架。NET,通常在单元测试中用于快速创建模仿实际对象的依赖项对象。本项目使用Moq来模拟ItemManager对象,以便在不需要数据库的情况下运行测试。

    注意:这个步骤不是示例测试所需要的,但是在大多数实际测试用例中是需要的。
    1. 在Visual Studio的解决方案资源管理器,右键单击单元测试项目。
    2. 选择管理Nuget包
    3. 搜索Moq并安装。

      Moq Nuget安装

    此示例创建一个MockStores类,与Moq一起使用来模拟数据库及其行为。

    创建一个名为模拟并创建一个MockStores.cs文件夹内的文件。输入以下代码MockStores.cs

    使用System.Collections.Generic;使用来;使用Dnn.Modules.CompanyName.MyMvcModule.Components;使用Dnn.Modules.CompanyName.MyMvcModule.Models;使用Moq;名称空间MyMvcModuleTests。模拟{ class MockStores { public static Mock MockItemManager() { var allItems = new List(); var mock = new Mock(); // void CreateItem(Item t); mock.Setup(x => x.CreateItem(It.IsAny())) .Callback((Item i) => { allItems.Add(i); }); // void DeleteItem(int itemId, int moduleId); mock.Setup(x => x.DeleteItem(It.IsAny(), It.IsAny())) .Callback((int id, int mid) => { var remItem = allItems.FirstOrDefault(i => i.ItemId == id && i.ModuleId == mid); allItems.Remove(remItem); }); // void DeleteItem(Item t); mock.Setup(x => x.DeleteItem(It.IsAny())) .Callback((Item di) => { var remItem = allItems.FirstOrDefault(i => i.ItemId == di.ItemId); allItems.Remove(remItem); }); // IEnumerable GetItems(int moduleId); mock.Setup(x => x.GetItems(It.IsAny())) .Returns((int mid) => allItems.Where(x => x.ModuleId == mid)); // Item GetItem(int itemId, int moduleId); mock.Setup(x => x.GetItem(It.IsAny(), It.IsAny())) .Returns((int id, int mid) => allItems.FirstOrDefault(i => i.ItemId == id && i.ModuleId == mid)); // void UpdateItem(Item t); mock.Setup(x => x.UpdateItem(It.IsAny())) .Callback((Item i) => { allItems.Add(i); }); return mock; } } }

    静态MockItemManager ()方法中的MockStores类的所有方法IItemManager实现。因此,MockStores可以在控制器中作为IItemManager实现。

    allItems变量的泛型列表对象,并用作数据存储。

  4. 创建单元测试。
    提示:单元测试方法名称应该比典型方法更具描述性。理想情况下,测试方法名称包括正在测试的方法名称、正在执行的测试以及预期的结果。例子:Edit_CreateNewItem_ModuleIdAssignedinModel可以是测试方法的名称,用于验证是否存在编辑()方法创建一个新项(如果该项尚不存在)moduleID在视图的模型中赋值。

    属性的单元测试ItemController类。

    您可以重命名示例单元测试文件UnitTest1.cs(在创建新的单元测试项目时作为默认包含)ItemControllerTests.cs或者创建一个新文件。然后输入以下代码ItemControllerTests.cs

    使用System.Web.Mvc;使用Dnn.Modules.CompanyName.MyMvcModule.Controllers;使用Dnn.Modules.CompanyName.MyMvcModule.Models;使用Microsoft.VisualStudio.TestTools.UnitTesting;使用MyMvcModuleTests.Mocks;命名空间MyMvcModuleTests {[TestClass]公共类ItemControllerTests {[TestMethod]公共无效Edit_CreateNewItem_ModuleIdAssignedinModel(){// 1 -安排int moduleId = 2;var mockData = MockStores.MockItemManager();var modTwoItemCntrl = new ItemController(mockData. var)对象,moduleId);//为moduleId=2的模块创建控制器// 2 - Act var actionResult = (ViewResult)modTwoItemCntrl.Edit(); // Call the edit view with no item Id (Add New). // 3 - Assert var itemModel = (Item)actionResult.Model; Assert.IsTrue(itemModel != null && itemModel.ModuleId == moduleId); } } }
    这个样本单元测试使用了单元测试的Arrange Act Assert模式:
    • 的一个新实例ItemController是用newMockStores。MockItemManager实例和moduleId.(下一步是改造ItemController构造函数来处理这个单元测试。
    • 行动:编辑()方法在不带任何参数的情况下调用该控件,并保存结果。
    • 断言:测试验证moduleId在呈现添加/编辑项视图之前,在结果View模块中设置。

    如果您的控制器具有更复杂的业务逻辑,您可以自动化单元测试的验证。

  5. 翻新的ItemController.Edit ()方法来处理单元测试。

    在使用MVC模块模板生成的代码中ItemController对数据层没有依赖项注入功能。此外,一些基本的DNN环境对象,如PortalSettings而且ModuleContext在运行单元测试时不可用。改造的ItemController.Edit ()方法修复了这些限制。

    类中添加构造函数和类变量ItemController注入IItemManager实现/模拟器和moduleId

    public ActionResult Edit(int itemId = -1){//忽略单元测试的注册错误。try {dotnetnuk . framework . javascriptlibraries . javascriptrequestregistration (CommonJs.DnnPlugins);} catch {} if (PortalSettings != null) {var userlist = UserController.GetUsers(PortalSettings. portalid);var users = from user in userlist.Cast(). tolist () select new SelectListItem {Text = user. var users = from user in userlist.Cast(). tolist ()DisplayName, Value = user.UserID.ToString()};ViewBag。Users =用户;} if (ModuleContext != null) {_moduleId = ModuleContext. moduleid;} var item = (itemId == -1) ?ItemManager.Instance. new Item {ModuleId = _moduleId}:GetItem (itemId _moduleId);返回视图(项); }

    有些行是默认的ItemController不适用于本单元测试示例,其中使用ItemController模拟。

    为了在运行单元测试时忽略这些行,改装的ItemController.Edit ()方法检查PortalSettings而且ModuleContext,它们从模块的运行时引擎获取它们的值。在单元测试期间,运行时引擎将被ItemController模拟;因此,这些变量被设置为当单元测试运行时。

    _moduleId变量传递给构造函数itemId参数通过样例单元测试和ItemManager。Instance将是我们的模拟实例,因为我们的单元测试覆盖了构造函数中的实现。

  6. 运行单元测试。
    你可以在Visual Studio中运行单元测试:
    • 测试>调试
    • 测试>运行

    测试资源管理器窗口可快速查看所有测试、测试结果以及运行这些测试的命令。


    测试资源管理器

    对于更高级的用户,可以将单元测试安排为作为构建流程的一部分自动运行。