Fel*_*ipe 2 c# asp.net-mvc unit-testing automapper justmock
我正在为我的ASP .NET MVC Controller类创建一些单元测试,我遇到了一些非常奇怪的错误:
我的控制器代码如下:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(JournalViewModel journal)
{
var selectedJournal = Mapper.Map<JournalViewModel, Journal>(journal);
var opStatus = _journalRepository.DeleteJournal(selectedJournal);
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)
我的测试代码如下:
[TestMethod]
public void Delete_Journal()
{
// Arrange
// Simulate PDF file
HttpPostedFileBase mockFile = Mock.Create<HttpPostedFileBase>();
Mock.Arrange(() => mockFile.FileName).Returns("Test.pdf");
Mock.Arrange(() => mockFile.ContentLength).Returns(255);
// Create view model to send.
JournalViewModel journalViewModel = new JournalViewModel();
journalViewModel.Id = 1;
journalViewModel.Title = "Test";
journalViewModel.Description = "TestDesc";
journalViewModel.FileName = "TestFilename.pdf";
journalViewModel.UserId = 1;
journalViewModel.File = mockFile; // Add simulated file
Mock.Arrange(() => journalRepository.DeleteJournal(null)).Returns(new OperationStatus
{
Status = true
});
// Act
PublisherController controller = new PublisherController(journalRepository, membershipRepository);
RedirectToRouteResult result = controller.Delete(journalViewModel) as RedirectToRouteResult;
// Assert
Assert.AreEqual(result.RouteValues["Action"], "Index");
}
Run Code Online (Sandbox Code Playgroud)
问题1 - 映射异常:
每次运行测试时,我都会收到以下异常:
测试名称:Delete_Journal测试
FullName:Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal
测试源:\ Source\Journals.Web.Tests\Controllers\PublisherControllerTest.cs:第132行
测试结果:失败测试持续时间:0 :00:00 ,3822468结果StackTrace:位于\ sources\Journals.Web\Controllers\PublisherController.cs中的Journals.Web.Controllers.PublisherController.Delete(JournalViewModel journal):位于Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal()的第81行\来源\\Journals.Web.Tests\Controllers\PublisherControllerTest.cs:第156行结果消息:测试方法Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal引发异常:AutoMapper.AutoMapperMappingException:缺少类型映射配置或不支持的映射.
映射类型:JournalViewModel - > Journal Journals.Model.JournalViewModel - > Journals.Model.Journal
目的地路径:期刊
来源价值:Journals.Model.JournalViewModel
它似乎有类之间的映射问题JournalViewModel和Journal,但是我不知道在哪里是.我将此代码添加到Application_Start中Global.asax.cs:
Mapper.CreateMap<Journal, JournalViewModel>();
Mapper.CreateMap<JournalViewModel, Journal>();
Run Code Online (Sandbox Code Playgroud)
从映射Journal到JournalViewModel工作正在进行中.
最后我尝试添加Mapper.CreateMap<JournalViewModel, Journal>();作为方法的第一行Delete然后一切正常,但我不知道为什么.
问题2 - HTML异常
使用上面的解决方法运行映射后,我遇到一个问题,即属性Statusfrom var opStatus = _journalRepository.DeleteJournal(selectedJournal);始终为false,即使我使用Mock覆盖它并使其始终为true.这会导致抛出不应发生的HTML异常.
编辑
我在Application_Start中更改为:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Journal, JournalViewModel>();
cfg.CreateMap<JournalViewModel, Journal>();
});
Run Code Online (Sandbox Code Playgroud)
但我仍然有同样的错误.
编辑 - 问题2解决了
事实证明,我忘了将映射添加到我的单元测试类,所以我做了以下事情:
[TestInitialize]
public void TestSetup()
{
// Create necessary mappings
Mapper.CreateMap<Journal, JournalViewModel>();
Mapper.CreateMap<JournalViewModel, Journal>();
//...other code omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)
事实证明,这是问题的根源.我认为,因为在单元测试中从不调用Global.asax.cs Application_Start(),所以从不创建Mapping,所以我必须在单元测试初始化中自己完成.
问题1
Automapper同时具有静态和实例API.您应该考虑使用实例API IMapper并将其注入控制器.
public class PublisherController : Controller {
private readonly IMapper mapper;
public PublisherController(IJournalRepository journalRepository, IMembershipRepositry membershipRepository, IMapper mapper) {
//...other code omitted for brevity
this.mapper = mapper;
}
//...other code omitted for brevity
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(JournalViewModel journal) {
var selectedJournal = mapper.Map<JournalViewModel, Journal>(journal);
var opStatus = _journalRepository.DeleteJournal(selectedJournal);
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
return RedirectToAction("Index");
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许根据需要更好地模拟/伪装/配置映射.您应确保配置IMapper依赖注入到控制器中.
如果您无法更改为实例api,则需要在运行测试之前确保映射器为Initialize
Mapper.Initialize(cfg => {
cgf.CreateMap<JournalViewModel, Journal>();
});
Run Code Online (Sandbox Code Playgroud)
问题2
你在考试中的安排是
Mock.Arrange(() => journalRepository.DeleteJournal(null)).Returns(new OperationStatus
{
Status = true
});
Run Code Online (Sandbox Code Playgroud)
这是因为您意识到不适用于journalRepository.DeleteJournal使用实际实例调用的情况.假设您正在使用来自Telerik的JustMock,您应该安排更灵活的参数.
Mock.Arrange(() => journalRepository.DeleteJournal(Arg.IsAny<Journal>())).Returns(new OperationStatus
{
Status = true
});
Run Code Online (Sandbox Code Playgroud)
资料来源:JustMock安排中的处理参数
完整测试:实例API
[TestMethod]
public void Delete_Journal() {
// Arrange
//Configure mapping just for this test but something like this
//should be in accessible from your composition root and called here.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Journal, JournalViewModel>();
cfg.CreateMap<JournalViewModel, Journal>();
});
var mapper = config.CreateMapper(); // IMapper
// Simulate PDF file
var mockFile = Mock.Create<HttpPostedFileBase>();
Mock.Arrange(() => mockFile.FileName).Returns("Test.pdf");
Mock.Arrange(() => mockFile.ContentLength).Returns(255);
// Create view model to send.
var journalViewModel = new JournalViewModel();
journalViewModel.Id = 1;
journalViewModel.Title = "Test";
journalViewModel.Description = "TestDesc";
journalViewModel.FileName = "TestFilename.pdf";
journalViewModel.UserId = 1;
journalViewModel.File = mockFile; // Add simulated file
var status = new OperationStatus {
Status = true
};
Mock.Arrange(() => journalRepository.DeleteJournal(Arg.IsAny<Journal>())).Returns(status);
var controller = new PublisherController(journalRepository, membershipRepository, mapper);
// Act
var result = controller.Delete(journalViewModel) as RedirectToRouteResult;
// Assert
Assert.AreEqual(result.RouteValues["Action"], "Index");
}
Run Code Online (Sandbox Code Playgroud)
完整测试:静态API
[TestMethod]
public void Delete_Journal() {
// Arrange
//Configure mapping just for this test but something like this
//should be in accessible from your composition root and called here.
Mapper.Initialize(cfg => {
cfg.CreateMap<Journal, JournalViewModel>();
cfg.CreateMap<JournalViewModel, Journal>();
});
// Simulate PDF file
var mockFile = Mock.Create<HttpPostedFileBase>();
Mock.Arrange(() => mockFile.FileName).Returns("Test.pdf");
Mock.Arrange(() => mockFile.ContentLength).Returns(255);
// Create view model to send.
var journalViewModel = new JournalViewModel();
journalViewModel.Id = 1;
journalViewModel.Title = "Test";
journalViewModel.Description = "TestDesc";
journalViewModel.FileName = "TestFilename.pdf";
journalViewModel.UserId = 1;
journalViewModel.File = mockFile; // Add simulated file
var status = new OperationStatus {
Status = true
};
Mock.Arrange(() => journalRepository.DeleteJournal(Arg.IsAny<Journal>())).Returns(status);
var controller = new PublisherController(journalRepository, membershipRepository);
// Act
var result = controller.Delete(journalViewModel) as RedirectToRouteResult;
// Assert
Assert.AreEqual(result.RouteValues["Action"], "Index");
}
Run Code Online (Sandbox Code Playgroud)