116
votes

I have a function in a controller that I am unit testing that expects values in the header of the http request. I can't initialize the HttpContext because it is readonly.

My controller function expects a http request header value for "device-id"

[TestMethod]
public void TestValuesController()
{
    ValuesController controller = new ValuesController();

    //not valid controller.HttpContext is readonly
    //controller.HttpContext = new DefaultHttpContext(); 

    var result = controller.Get();
    Assert.AreEqual(result.Count(), 2);
}

Is there a straight-forward way to do this without using a third party library?

2
Don't use HttpContext? The whole point of using controllers is that the data comes through the controller's parameters. If your controller uses the HttpContext to read data as if it was a WebForms page, you have a problem.Panagiotis Kanavos
@PanagiotisKanavos The value in the header is a piece of information indicating which mobile device the call comes from. This is necassary to retrieve the correct data. The device ID is in the header because the ID is needed for authentication, which is handled by a custom action filter. I could could pass the device ID as a route parameter but it would be redundantJames Wierzba
Check FromHeaderAttribute but also check the duplicate. HttpContext is injectable through configuration nowPanagiotis Kanavos
I suggest you edit your question to specify exactly what you want (access to header fields to identify mobile devices). The ASP.NET documentation seems to be going through a .... "transition" period to put it kindly, with documentation pages missing. Check this almost identical question that asks how to route mobile devicesPanagiotis Kanavos

2 Answers

259
votes

I was able to initialize the httpcontext and header in this way:

[TestMethod]
public void TestValuesController()
{
    ValuesController controller = new ValuesController();
    controller.ControllerContext = new ControllerContext();
    controller.ControllerContext.HttpContext = new DefaultHttpContext();
    controller.ControllerContext.HttpContext.Request.Headers["device-id"] = "20317";
    var result = controller.Get();
    //the controller correctly receives the http header key value pair device-id:20317
    ...
}
21
votes

Rather than mocking out the HTTPContext, it is probably a better idea to map the header into a parameter on the method. For example, in the controller at the bottom of this answer, the id parameter is set to the value header with a name equal to "device-id"... The unit test then becomes

[TestMethod]
public void TestValuesController()
{
    ValuesController controller = new ValuesController();
    var result = controller.GetHeaderValue("27");
    Assert.AreEqual(result, "27");
}

While you can mock the HttpContext, in my opinion it is something that should be avoided unless you have no choice. The documentation for the FromHeaderAttribute can be found here FromHeaderAttribute Class.

public class ValuesController: Controller
{
    public string GetHeaderValue([FromHeader(Name = "device-id")] string id)
    {
        return id;
    }
}