2016-10-05 5 views
2

Я создаю несколько модульных тестов для моего класса ASP .NET MVC контроллер, и я столкнулся с некоторыми очень странные ошибки:Проблемы создания модульного тестирования для ASP .NET MVC

Мой код контроллер находится ниже:

[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"); 
} 

Мой тестовый код ниже:

[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"); 
} 

Задача 1 - Mapping Исключение:

Каждый раз, когда я запускаю мой тест я получаю следующее исключение:

Имя теста: Delete_Journal Тест
FullName: Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal
Test Источник: \ Source \ Журналы .Web.Tests \ Контроллеры \ PublisherControllerTest.cs : линия 132
Тест Результат: Failed Test Продолжительность: 0: 00: 00,3822468

Результат StackTrace: в Journals.Web.Controllers.PublisherController.Delete (J ournalViewModel журнал) в \ Source \ Journals.Web \ Контроллеры \ PublisherController.cs: строка 81 в Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal() в \ Source \ Journals.Web.Tests \ Контроллеры \ PublisherControllerTest.cs: линия 156 Результат сообщение: метод испытания Journals.Web.Tests.Controllers.PublisherControllerTest.Delete_Journal бросил исключение: AutoMapper.AutoMapperMappingException: Отсутствует тип конфигурации карта или не поддерживается отображение.

типы карт: JournalViewModel -> Журнал Journals.Model.JournalViewModel -> Journals.Model.Journal

назначения Путь: Журнал

Источник значение: Journals.Model.JournalViewModel

Кажется, что существует проблема сопоставления между классами JournalViewModel и Journal, однако я не знаю, где это. Я добавил этот код на Application_Start в Global.asax.cs:

Mapper.CreateMap<Journal, JournalViewModel>(); 
Mapper.CreateMap<JournalViewModel, Journal>(); 

и отображение Journal в JournalViewModel работает.

В конце я попытался добавить Mapper.CreateMap<JournalViewModel, Journal>(); в качестве первой строки метода Delete, а затем все работает, однако я не знаю, почему.

Задача 2 - HTML Исключение

После того, как отображение работает с выше обходным путем, у меня есть проблема, в которой свойство Status из var opStatus = _journalRepository.DeleteJournal(selectedJournal); всегда ложно, даже если я использовал Mock, чтобы переопределить его и сделать это всегда так. Это вызывает выброс HTML Exception, которого не должно произойти.

EDIT

Я изменил в моей Application_Start на:

Mapper.Initialize(cfg => 
{ 
    cfg.CreateMap<Journal, JournalViewModel>(); 
    cfg.CreateMap<JournalViewModel, Journal>(); 
}); 

Но я до сих пор ту же ошибку.

EDIT - Проблема 2 решаемые

Оказывается, что я забыл добавить отображение на мой модульного тестирования класса, поэтому я сделал следующее:

[TestInitialize] 
public void TestSetup() 
{ 
    // Create necessary mappings 
    Mapper.CreateMap<Journal, JournalViewModel>(); 
    Mapper.CreateMap<JournalViewModel, Journal>(); 

    //...other code omitted for brevity 
} 

И получается, что это был источником проблемы. Я думаю, что поскольку приложение Global.asax.cs Application_Start() никогда не вызывается в модульных тестах, сопоставление никогда не создается, поэтому я должен был сделать это сам в инициализации модульных тестов.

+2

Вы, наверное, нужна какая-то 'TestBase' класс wh если вы вызываете свой код конфигурации отображения. Статический «Mapper» в вашем тесте ничего не знает о конфигурации сопоставления из вашего «Global.asax.cs». –

+0

Я бы предложил использовать статический метод для создания всех карт, а затем вызвать этот статический метод как часть вашей тестовой настройки. Что касается второй проблемы, не могли бы вы сообщить мне, что такое JournalRepository. Вы передаете это как зависимость? –

+0

Какую версию Automapper вы используете?Я бы предложил использовать абстракции («IMapper») вместо статического API, поскольку вы уже обнаружили трудности при тестировании статических API-модулей – Nkosi

ответ

3

Задача 1

Automapper имеет как Static and Instance 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"); 
    } 
} 

Это позволило бы лучше высмеивать/подделывать/конфигурировать картографию по мере необходимости. Убедитесь, что вы сконфигурировали IMapper для инъекций зависимостей в контроллерах.

, если вы не в состоянии изменить к API экземпляров, то вам необходимо убедиться, что преобразователь является Initialize перед запуском тестов

Mapper.Initialize(cfg => { 
    cgf.CreateMap<JournalViewModel, Journal>(); 
}); 

Задача 2

ваше расположение в тесте

Mock.Arrange(() => journalRepository.DeleteJournal(null)).Returns(new OperationStatus 
{ 
    Status = true 
}); 

Это, как вы поняли, не будут работать для тех случаев, когда вы называете journalRepository.DeleteJournal с ACTU al instance. Предполагая, что вы используете JustMock от Telerik, вы должны принять более гибкий аргумент.

Mock.Arrange(() => journalRepository.DeleteJournal(Arg.IsAny<Journal>())).Returns(new OperationStatus 
{ 
    Status = true 
}); 

Источник: Handling Arguments in JustMock Arrangements

Полная проверка: Instance 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"); 
} 

Полная проверка: Статический 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"); 
} 
+0

Привет, я сменил свой тестовый код на проблему 2, и все сработало! :) – Felipe

+1

Что касается проблемы 1, есть ли способ заставить Mapper работать со статическими методами? В противном случае мне пришлось бы изменить много моего текущего кода. – Felipe

+1

Проверьте обновленный ответ. инициализируйте картограф перед запуском теста. Если ответ будет полезен для голосования. Ответ решает вашу проблему, а затем отмечаю как ответ – Nkosi

 Смежные вопросы

  • Нет связанных вопросов^_^