How to get and set http headers in Action, a testable way

I have an action that returns either FileContentResult or NotModifiedResult, which is a custom result type that returns HTTP 304 to indicate that the requested resource has not been modified, for example:

[ReplaceMissingPicture(Picture = "~/Content/Images/nothumbnail.png", MimeType = "image/png")] public ActionResult Thumbnail(int id) { var item = Service.GetItem(id); var requestTag = Request.Headers["If-None-Match"] ?? string.Empty; var tag = Convert.ToBase64String(item.Version.ToArray()); if (tag == requestTag) { return new NotModifiedResult(); } if (item.Thumbnail != null) { var thumbnail = item.Thumbnail.ToArray(); var mime = item.PictureMime; Response.AppendHeader("ETag", tag); return File(thumbnail, mime); } else { return null; } } 

This action should gain access to the Response object, which, of course, is not present during testing, so this action is irreplaceable. I could add conditional statements around it so that it runs during testing, but then I cannot verify that the headers are set correctly.

What will be the solution to this problem?

FYI, the ReplaceMissingPicture filter returns a specific resource in case null null was returned from this action in order to call MapPath () from the controller for the same reason.

+4
source share
2 answers

The first step is to create an interface that simplifies the services you need: -

  public interface IHeaders { public string GetRequestHeader(string headerName); public void AppendResponseHeader(string headerName, string headerValue); } 

Now create a default implementation: -

  public Headers : IHeaders { public string GetRequestHeader(string headerName) { return HttpContext.Current.Request[headerName]; } public void AppendResponseHeader(string headerName, string headerValue) { HttpContext.Current.Response.AppendHeader(headerName, headerValue); } } 

Now add a new field to your controller: -

  private IHeaders myHeadersService; 

add a new constructor to you: -

  public MyController(IHeaders headersService) { myHeadersService = headersService; } 

change or add default constructor: -

  public MyController() { myHeadersService = new Headers(); } 

now in your action code use myHeadersService instead of Response and Request objects.

In your tests, create your own implementation of the IHeaders interface to emulate / test the action code and pass this implementation when building the controller.

+1
source

What about subclassing FileResult - say ETagFileResult - which in its ExecuteResult() method sets the ETag header and then uses the base class implementation by default? You can test this class with a laughed context (as you presumably with your NotModifiedResult ) to be sure that it is doing the right thing. And remove all the complications from testing the controller.

Otherwise, in test mode, you can set the mocking context on the controller (after creating the class instance before calling the action method). See this question for example. But that seems like a lot of work.

(Also, by the way, it looks like you are specifying the tag value twice: once when the tag installed, and once more when you actually set the title ....)

+1
source

All Articles