如何避免 HttpContext.Server.MapPath 用于单元测试目的
How to avoid HttpContext.Server.MapPath for Unit Testing Purposes
我正在 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 并使其不为空,但 Server
是 null
,我想我也可以继续模拟它(我不知道how yet),但是有没有更好的办法呢?如果需要,我可以进行重大重构。
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);
}
此服务现在足够灵活,可以根据需要从网络服务等其他来源获取文件。
我正在 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 并使其不为空,但 Server
是 null
,我想我也可以继续模拟它(我不知道how yet),但是有没有更好的办法呢?如果需要,我可以进行重大重构。
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);
}
此服务现在足够灵活,可以根据需要从网络服务等其他来源获取文件。