如何避免unit testing目的的HttpContext.Server.MapPath

我在ASP.net MVC 5应用程序中工作。 我想unit testing我的控制器动作,看起来像这样

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

我计划对这个特定的动作做很多unit testing,但是我从一个非常简单的unit testing开始,它检查是否返回了正确类型的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来模拟各种类。

什么是unit testing的最佳方法? Darin在这个post中建议我们避免使用HttpContext.Current如果我们想要我们的代码unit testing。

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

HttpContext.Server.MapPath将需要一个底层虚拟目录提供程序,它在unit testing期间不存在。 摘要可以模拟的服务背后的路径映射,以使代码可测试。

 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服务)获取文件。