Testing a Web API method that uses HttpContext.Current.Request.Files?

I am trying to write a test for a Web API method that uses HttpContext.Current.Request.Files , and after exhaustive searching and experimenting, I cannot figure out how to taunt him. The test method is as follows:

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

I understand that there are other issues similar to this , but they do not affect this particular situation,

If I try to make fun of the context, I run into problems with the Http* object hierarchy. Let's say I created various mock objects (using Moq ) as follows:

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

Trying to assign it to the current context ...

 HttpContext.Current = mockContext.Object; 

... results in a / redline compiler error because it Cannot convert source type 'System.Web.HttpContextBase' to target type 'System.Web.HttpContext' .

I also tried drilling various context objects that come with the constructed controller object, but cannot find one that: a) is the return object of the HttpContext.Current call in the body of the controller method and b) provides access to standard HttpRequest properties, such as Files .

 var requestMsg = controller.Request; //returns HttpRequestMessage var context = controller.ControllerContext; //returns HttpControllerContext var requestContext = controller.RequestContext; //read-only returns HttpRequestContext 

It is also important to note that I cannot change the controller that I am testing at all, so I cannot change the constructor so that the context can be entered.

Is there any way to trick HttpContext.Current.Request.Files for unit testing in web API?

Update
Although I'm not sure if this will be accepted by the team, I am experimenting with modifying the Post method to use Request.Content , as suggested by Martin Liversage . Currently, it looks something like this:

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

My test looks something like this:

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

Now I get the ReadAsMultipartAsync error 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.

+9
c # unit-testing asp.net-web-api moq
source share
3 answers

The web API was created to support unit testing, allowing you to simulate various context objects. However, using HttpContext.Current you use the "old style" of System.Web code which uses the HttpContext class which makes unit testing of your code impossible.

In order for your code to be a testable module, you must stop using HttpContext.Current . In the Submitting HTML Form Data to ASP.NET Web API: Uploading Files and Multi-Component MIME section, you can see how to upload files using the Web API. Ironically, this code also uses HttpContext.Current to access MapPath but in the web API you should use HostingEnvironment.MapPath which also works outside of IIS. Taunting the latter is also problematic, but for now I will focus on your question about taunting the request.

HttpContext.Current using HttpContext.Current allows you to perform unit testing of your controller by setting the ControllerContext property of the controller:

 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; 
+25
source

The accepted answer is perfect for the OP question. I wanted to add my solution here, which comes from Martin, as this is the page I was directed to when I was just looking for how to deflate the Request object for the Web API so that I can add the headers that my controller is looking for. It was hard for me to find a simple answer:

  var controllerContext = new HttpControllerContext(); controllerContext.Request = new HttpRequestMessage(); controllerContext.Request.Headers.Add("Accept", "application/xml"); MyController controller = new MyController(MockRepository); controller.ControllerContext = controllerContext; 

And you are here; A very simple way to create a controller context with which you can "uncork" the Request object and provide the correct headers for your controller method.

+2
source

I only scoffed at the published file. I believe that all files can also be tampered with this way.

It was in my controller

 private HttpPostedFileBase _postedFile; /// <summary> /// For mocking HttpPostedFile /// </summary> public HttpPostedFileBase PostedFile { get { if (_postedFile != null) return _postedFile; if (HttpContext.Current == null) { throw new InvalidOperationException("HttpContext not available"); } return new HttpContextWrapper(HttpContext.Current).Request.Files[0]; } set { _postedFile = value; } } [HttpPost] public MyResponse Upload() { if (!ValidateInput(this.PostedFile)) { return new MyResponse { Status = "Input validation failed" }; } } private bool ValidateInput(HttpPostedFileBase file) { if (file.ContentLength == 0) return false; if (file.ContentType != "test") return false; if (file.ContentLength > (1024 * 2048)) return false; return true } 

It was in my unit test.

 public void Test1() { var controller = new Mock<MyContoller>(); controller.Setup(x => x.Upload).Returns(new CustomResponse()); controller.Request = new HttpRequestMessage(); controller.Request.Content = new StreamContent(GetContent()); controller.PostedFile = GetPostedFile(); var result = controller.Upload().Result; } private HttpPostedFileBase GetPostedFile() { var postedFile = new Mock<HttpPostedFileBase>(); using (var stream = new MemoryStream()) using (var bmp = new Bitmap(1, 1)) { var graphics = Graphics.FromImage(bmp); graphics.FillRectangle(Brushes.Black, 0, 0, 1, 1); bmp.Save(stream, ImageFormat.Jpeg); postedFile.Setup(pf => pf.InputStream).Returns(stream); postedFile.Setup(pf => pf.ContentLength).Returns(1024); postedFile.Setup(pf => pf.ContentType).Returns("bmp"); } return postedFile.Object; } 

Although I could not successfully mock HTTPContext. But I was able to make fun of downloading a file.

-one
source

All Articles