Как избежать 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);
}
Этот сервис теперь достаточно гибкий, чтобы при необходимости получать файл из других источников, таких как веб-сервис.