测试使用HttpContext.Current.Request.Files的Web API方法?

AJ.*_*AJ. 10 c# unit-testing moq httpcontext asp.net-web-api

我正在尝试编写一个Web API方法的测试,该方法使用HttpContext.Current.Request.Files并经过详尽的搜索和实验后,我无法弄清楚如何模拟它.正在测试的方法如下所示:

[HttpPost]
public HttpResponseMessage Post()
{
    var requestFiles = HttpContext.Current.Request.Files;
    var file = requestFiles.Get(0);
    //do some other stuff...
}
Run Code Online (Sandbox Code Playgroud)

我意识到还有其他 类似的问题,但它们没有解决这个具体情况.

如果我试图模拟上下文,我会遇到Http*对象层次结构的问题.假设我设置了各种模拟对象(使用Moq),如下所示:

var mockFiles = new Mock<HttpFileCollectionBase>();
mockFiles.Setup(s => s.Count).Returns(1);
var mockFile = new Mock<HttpPostedFileBase>();
mockFile.Setup(s => s.InputStream).Returns(new MemoryStream());
mockFiles.Setup(s => s.Get(It.IsAny<int>())).Returns(mockFile.Object);
var mockRequest = new Mock<HttpRequestBase>();
mockRequest.Setup(s => s.Files).Returns(mockFiles.Object);
var mockContext = new Mock<HttpContextBase>();
mockContext.Setup(s => s.Request).Returns(mockRequest.Object);
Run Code Online (Sandbox Code Playgroud)

试图将其分配给当前上下文...

HttpContext.Current = mockContext.Object;
Run Code Online (Sandbox Code Playgroud)

...导致编译器错误/红线,因为它Cannot convert source type 'System.Web.HttpContextBase' to target type 'System.Web.HttpContext'.

我也尝试钻进构造的控制器对象附带的各种上下文对象,但找不到a)是HttpContext.Current控制器方法体中调用的返回对象,b)提供对标准HttpRequest属性的访问,例如Files.

var requestMsg = controller.Request;   //returns HttpRequestMessage
var context = controller.ControllerContext;  //returns HttpControllerContext
var requestContext = controller.RequestContext;   //read-only returns HttpRequestContext
Run Code Online (Sandbox Code Playgroud)

同样重要的是要注意我无法更改我正在测试的控制器,因此我无法更改构造函数以允许注入上下文.

有没有办法模拟HttpContext.Current.Request.FilesWeb API中的单元测试?

更新
虽然我不确定这将被团队接受,但我正在尝试更改Post方法Request.Content,正如Martin Liversage所建议的那样.它目前看起来像这样:

public async Task<HttpResponseMessage> Post()
{
    var uploadFileStream = new MultipartFormDataStreamProvider(@"C:\temp");
    await Request.Content.ReadAsMultipartAsync(uploadFileStream);
    //do the stuff to get the file
    return ActionContext.Request.CreateResponse(HttpStatusCode.OK, "it worked!");
}
Run Code Online (Sandbox Code Playgroud)

我的测试看起来与此类似:

var byteContent = new byte[]{};
var content = new MultipartContent { new ByteArrayContent(byteContent) };
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext 
{
    Request = new HttpRequestMessage
        {
            Content = new MultipartContent { new ByteArrayContent(byteContent) }
        }
};
Run Code Online (Sandbox Code Playgroud)

现在我收到错误ReadAsMultipartAsync:

System.IO.IOException: Error writing MIME multipart body part to output stream. ---> System.InvalidOperationException: The stream provider of type 'MultipartFormDataStreamProvider' threw an exception. ---> System.InvalidOperationException: Did not find required 'Content-Disposition' header field in MIME multipart body part.

Mar*_*age 27

通过允许您模拟各种上下文对象,构建Web API以支持单元测试.但是,通过使用,HttpContext.Current您使用的是"旧式" System.Web代码,该代码HttpContext使得无法对代码进行单元测试.

为了使您的代码可以进行单元测试,您必须停止使用HttpContext.Current.在ASP.NET Web API中发送HTML表单数据:文件上载和多部分MIME,您可以看到如何使用Web API上载文件.具有讽刺意味的是,此代码也用于HttpContext.Current访问MapPath但您应该使用的Web API HostingEnvironment.MapPath也可以在IIS之外使用.嘲笑后者也存在问题但是现在我关注的是关于嘲笑请求的问题.

不使用HttpContext.Current允许您通过分配控制器的ControllerContext属性来对控制器进行单元测试:

var content = new ByteArrayContent( /* bytes in the file */ );
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext {
  Request = new HttpRequestMessage {
    Content = new MultipartContent { content }
  }
};
var controller = new MyController();
controller.ControllerContext = controllerContext;
Run Code Online (Sandbox Code Playgroud)