如何避免单元测试目的的HttpContext.Server.MapPath

时间:2016-07-19 08:06:02

标签: c# unit-testing asp.net-mvc-5 rhino-mocks

我在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来模拟各种类。

单位测试的最佳方法是什么?如果我们想要我们的代码单元测试,那么this thread中的Darin建议我们避免使用HttpContext.Current

顺便说一句,我尝试模拟HttpContext并使其不为null,但是Servernull,我可以继续嘲笑我也认为(我不知道)怎么样),但没有更好的方法吗?如果需要,我进行重大重构没问题。

1 个答案:

答案 0 :(得分:4)

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

此服务现在足够灵活,可以根据需要从其他来源(如Web服务)获取文件。