【问题标题】:How to avoid HttpContext.Server.MapPath for Unit Testing Purposes如何避免 HttpContext.Server.MapPath 用于单元测试目的
【发布时间】:2016-11-22 00:09:57
【问题描述】:

我正在使用 ASP.net MVC 5 应用程序。我想对我的控制器动作进行单元测试,看起来像这样

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

  return View(vm);
}

所有繁重的工作都由SetupSearchViewModel() 方法完成,它本身就是一个编排器调用许多不同的其他方法,其中一个就是this

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 并使其不为空,但是 Servernull,我想我也可以继续模拟(我还不知道如何),但是没有更好的办法吗?如果需要,我可以进行重大重构。

【问题讨论】:

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


    【解决方案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);
    }
    

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

    【讨论】:

      猜你喜欢
      • 2015-05-10
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多