2

I saw the very same issue in this post but none of the solutions works now in ASP.Net Core 2.2. When I debug the unit test the Response property is still null and thus the test fails. I have been reading the asp.net core docs for an answer how to mock the ControllerContext so that the Response property has a value but I couldn't find anything working.

Here is the line inside the action that makes troubles:

Response.Headers.Add("Access-Control-Expose-Headers", "Content-Range");

So what I have ended up with in the unit test set up is:

        var routeData = new RouteData();
        routeData.Values.Add("controller", "Home");

        var headerDictionary = new HeaderDictionary();
        var response = new Mock<HttpResponse>();
        response.SetupGet(r => r.Headers).Returns(headerDictionary);

        var httpContext = new Mock<HttpContext>();
        httpContext.SetupGet(a => a.Response).Returns(response.Object);

        var actionContext = new ActionContext(
            httpContext.Object,
            routeData, 
            new ControllerActionDescriptor());

        _controller.ControllerContext = new ControllerContext(actionContext);
Kia Kaha
  • 1,565
  • 1
  • 17
  • 39

2 Answers2

2

Finally I have managed to mock the Controller's HttpContext with the following code:

    protected void SetUpControllerContext(ClaimsPrincipal principal)
    {
        var headerDictionary = new HeaderDictionary();
        var response = new Mock<HttpResponse>();
        response.SetupGet(r => r.Headers).Returns(headerDictionary);

        var httpContext = new Mock<HttpContext>();
        httpContext.SetupGet(a => a.Response).Returns(response.Object);
        httpContext.SetupGet(a => a.User).Returns(principal);


        this.SutController.ControllerContext = new ControllerContext()
        {
            HttpContext = httpContext.Object
        };
    }

Now altering the Response property inside Controller's actions is allowed and doesn't trigger an error.

Kia Kaha
  • 1,565
  • 1
  • 17
  • 39
1

A lot of the setup can be avoided by using the DefaultHttpContext which would have the needed properties already populated. This includes the Response and its members

//..

var routeData = new RouteData();
routeData.Values.Add("controller", "Home");

var httpContext = DefaultHttpContext(); //<--

var actionContext = new ActionContext(
    httpContext,
    routeData, 
    new ControllerActionDescriptor());

_controller.ControllerContext = new ControllerContext(actionContext);

//...

After exercising the subject under test, the response can the obtained from the context used by the controller and desired behavior asserted.

//...

//Assert
var response = httpContext.Response;
var key = "Access-Control-Expose-Headers";
Assert.True(response.Headers.TryGetValues(key, out var value));
Assert.Equals("Content-Range", value.FirstOrDefault()
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thank you for your answer - I have tried this also but stil got a null value for the Response property. Otherwise - yes, saves some effort. – Kia Kaha Feb 29 '20 at 12:45
  • @KiaKaha then you may not have provided the controller under test with the necessary dependencies for it to behave as expected when tested. According to the [source code](https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ControllerBase.cs#L50), The response is taken directly from the `HttpContext` and is not null by default. – Nkosi Feb 29 '20 at 12:54