Как избежать HttpContext.Server.MapPath для целей модульного тестирования

Я работаю в приложении ASP.net MVC 5. Я хотел бы провести модульное тестирование моего действия контроллера, которое выглядит следующим образом

public ActionResult Search()
{
  var vm = SetupSearchViewModel();

  return View(vm);
}

Вся тяжелая работа выполняется SetupSearchViewModel() метод, который сам по себе является оркестратором, вызывающим множество других методов, один из которых это

private string ExtractJsonFile(string filename)
{
  var filePath = HttpContext.Server.MapPath(filename);
  var json = System.IO.File.ReadAllText(filePath);
  return json;
}

Я планирую провести много модульных тестов для этого конкретного действия, но я начинаю с очень простого модульного теста, который проверяет, что возвращается правильный тип ActionResult.

[Test]
public void Search_Get_ReturnsViewResult()
{
  // arrange
  var performanceController = PerformanceControllerInstance;
  // act
  var result = performanceController.Search();
  //assert
  Assert.IsNotNull(result as ViewResult);
}

Тест не пройден из-за ExtractJsonFile метод. Оно использует HttpContext и это null, Я использую Rhino Mocks для насмешек над различными классами.

Что было бы лучшим способом для модульного тестирования этого? Дарин в этой теме советую избегать HttpContext.Current если мы хотим, чтобы наш код тестировался модулем.

Кстати, я попытался издеваться над HttpContext и сделал его не нулевым, но тогда Server является nullЯ могу пойти дальше и посмеяться над этим, я полагаю (пока не знаю, как), но нет ли лучшего способа? У меня нет проблем, если мне нужно сделать серьезный рефакторинг.

1 ответ

Решение

HttpContext.Server.MapPath потребует основного поставщика виртуальных каталогов, который не будет существовать во время модульного теста. Абстрактное отображение пути за сервисом, который вы можете использовать для проверки кода.

public interface IPathProvider {
    string MapPath(string path);
}

В реализации конкретного сервиса вы можете сделать свой звонок, чтобы сопоставить путь и получить файл.

public class ServerPathProvider: IPathProvider {
    public string MapPath(string path) {
        return HttpContext.Current.Server.MapPath(path);
    }
}

вы бы вставили абстракцию в свой контроллер или, где это необходимо, и использовали

public MyController : Controller {

    public MyController(IPathProvider pathProvider) {
        this.pathProvider = pathProvider;
    }

    //...other code removed for brevity

    private string ExtractJsonFile(string filename) {
      var filePath = pathProvider.MapPath(filename);
      var json = System.IO.File.ReadAllText(filePath);
      return json;
    }
}

Используя ваш фальшивый фреймворк, вы можете затем дразнить провайдера

[Test]
public void Search_Get_ReturnsViewResult() {
  // arrange
  IPathProvider mockedPathProvider = //...insert your mock/fake/stub here
  var performanceController = PerformanceControllerInstance(mockedPathProvider);
  // act
  var result = performanceController.Search();
  //assert
  Assert.IsNotNull(result as ViewResult);
}

и не быть связанным с HttpContext

Вы могли бы даже пойти дальше и рефакторинг всего ExtractJsonFile(string filename) в свой собственный сервис, чтобы обойти быть привязанным к диску, а также.

public interface IJsonProvider {
    string ExtractJsonFile(string filename);
}

Этот сервис теперь достаточно гибкий, чтобы при необходимости получать файл из других источников, таких как веб-сервис.

Другие вопросы по тегам