使用Automapper映射ViewModel之后我应该测试什么?
我试图测试控制器的Index
操作。 该操作使用AutoMapper将域Customer
对象映射到视图模型TestCustomerForm
。 虽然这有效,但我担心测试我从Index
操作中获得的结果的最佳方法。
控制器的索引操作如下所示:
public ActionResult Index() { TestCustomerForm cust = Mapper.Map(_repository.GetCustomerByLogin(CurrentUserLoginName)); return View(cust); }
它的TestMethod
看起来像这样:
[TestMethod] public void IndexShouldReturnCustomerWithMachines() { // arrange var customer = SetupCustomerForRepository(); // gets a boiler plate customer var testController = CreateTestController(); // act ViewResult result = testController.Index() as ViewResult; // assert Assert.AreEqual(customer.MachineList.Count(), (result.ViewData.Model as TestCustomerForm).MachineList.Count()); }
在CreateTestController
方法中,我使用Rhino.Mocks
来模拟客户存储库并将其设置为从SetupCustomerForRepository
返回客户。 通过这种方式,我知道当Index
操作调用_repository.GetCustomerByLogin(CurrentUserLoginName)
时,存储库将返回目标客户。 因此,我认为断言相等的计数足以满足IndexShouldReturnCustomerWithMachines
。
所有这些都说我担心我应该测试什么。
- 将
result.ViewData.Model as TestCustomerForm
似乎result.ViewData.Model as TestCustomerForm
。 这真的是一个问题吗? 这让我很担心,因为在这种情况下,我并没有真正进行测试驱动开发,而且我似乎依靠特定的实现来满足测试。 - 是否有更合适的测试来确保正确的映射?
- 我应该从
TestCustomerForm
测试每个映射属性吗? - 我应该做更多一般的控制器动作测试吗?
这是我们将AutoMapper移动到自定义ActionResult或ActionFilter的原因之一。 在某些时候,您只想测试您将Foo映射到FooDto,但不一定要测试实际的映射。 通过将AutoMapper移动到图层边界(例如控制器和视图之间),您可以只测试您告诉AutoMapper要执行的操作。
这类似于测试ViewResult。 您不从控制器测试视图是否已呈现,而是您告诉MVC呈现此类视图。 我们的行动结果变为:
public class AutoMapViewResult : ActionResult { public Type SourceType { get; private set; } public Type DestinationType { get; private set; } public ViewResult View { get; private set; } public AutoMapViewResult(Type sourceType, Type destinationType, ViewResult view) { SourceType = sourceType; DestinationType = destinationType; View = view; } public override void ExecuteResult(ControllerContext context) { var model = Mapper.Map(View.ViewData.Model, SourceType, DestinationType); View.ViewData.Model = model; View.ExecuteResult(context); } }
使用基本控制器类上的辅助方法:
protected AutoMapViewResult AutoMapView(ViewResult viewResult) { return new AutoMapViewResult(viewResult.ViewData.Model.GetType(), typeof(TDestination), viewResult); }
然后,控制器现在只指定要映射到/来自的内容,而不是执行实际映射:
public ActionResult Index(int minSessions = 0) { var list = from conf in _repository.Query() where conf.SessionCount >= minSessions select conf; return AutoMapView(View(list)); }
此时,我只需要测试“确保将此Foo对象映射到此目标FooDto类型”,而无需实际执行映射。
编辑:
以下是测试代码段的示例:
var actionResult = controller.Index(); actionResult.ShouldBeInstanceOf(); var autoMapViewResult = (AutoMapViewResult) actionResult; autoMapViewResult.DestinationType.ShouldEqual(typeof(EventListModel[])); autoMapViewResult.View.ViewData.Model.ShouldEqual(queryResult); autoMapViewResult.View.ViewName.ShouldEqual(string.Empty);
我可能会通过引入一个抽象来分离AutoMapper
和控制器之间的耦合:
public interface IMapper { TDest Map(TSource source); } public CustomerToTestCustomerFormMapper: IMapper { static CustomerToTestCustomerFormMapper() { // TODO: Configure the mapping rules here } public TestCustomerForm Map(Customer source) { return Mapper.Map(source); } }
接下来,将其传递给控制器:
public HomeController: Controller { private readonly IMapper _customerMapper; public HomeController(IMapper customerMapper) { _customerMapper = customerMapper; } public ActionResult Index() { TestCustomerForm cust = _customerMapper.Map( _repository.GetCustomerByLogin(CurrentUserLoginName) ); return View(cust); } }
在您的unit testing中,您将使用您最喜欢的模拟框架来存根此映射器。
- 将属性映射到集合项
- 数据实体>域对象> ViewModels,每个都具有截然不同的数据结构
- 如何为每个AppDomain配置一次AutoMapper
- AutoMapping自定义通用类型 – 如何?
- 在IQueryable上调用ProjectTo ()时,AutoMapper抛出StackOverflowException
- 如何使用AutoMapper将Dto映射到具有嵌套对象的现有对象实例?
- Automapper ResolveUsing导致“无法将此解析为可查询表达式”
- AutoMapper.dll中出现“AutoMapper.AutoMapperMappingException”类型的例外,但未在用户代码中处理
- AutoMapper 3.1.1和Entity Framework 6.1代理对象