3

I would like to unit test the next method:

public static async Task SetResponseBody(HttpResponse response, string message)
{   
    var originalResponseBody = response.Body;
    var responseBody = new MemoryStream();
    response.Body = responseBody;
    response.ContentType = "application/json";
    dynamic body = new { Message = message };
    string json = JsonSerializer.Serialize(body);
    await response.WriteAsync(json);
    response.Body.Seek(0, SeekOrigin.Begin);
    await responseBody.CopyToAsync(originalResponseBody);         
}

The last two lines are used from this post.

The current unit test implementation is:

[TestMethod]
public async Task SetResponseBody_TestMessageAsync()
{
    var expected = "TestMessage";
    string actual = null;
    var responseMock = new Mock<HttpResponse>();
    responseMock
        .Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
        .Callback((byte[] data, int offset, int length, CancellationToken token) =>
        {
            if (length > 0)
                actual = Encoding.UTF8.GetString(data);
        })
        .Returns(Task.CompletedTask);
    await ResponseRewriter.SetResponseBody(responseMock.Object, expected);
}

The unit tests fails due to a NullReferenceException which is raised once the test hits the 'await response.WriteAsync(json);' line of code. Could you point me in the right direction in order to fix this exception, so the test will pass?

Summarized: The unit tests needs to check if the given 'TestMessage' is actually written to the Body of the response.

Background information: I'm calling the SetResponseBody method in order to modify the response body as soon as the 'OnRedirectToIdentityProvider' event is raised from AddOpenIdConnect.

OnRedirectToIdentityProvider = async e =>
{
    // e is of type RedirectContext
    if (e.Request.Path.StartsWithSegments("/api")))
    {            
        if (e.Response.StatusCode == (int)HttpStatusCode.OK)
        {
            e.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            // TestMessage is a const
            // e.Response is readonly (get) so it's not possible to set it directly.
            await ResponseRewriter.SetResponseBody(e.Response, TestMessage);
        }
        e.HandleResponse();
    }
    await Task.CompletedTask;
}

.NET Core 3.1, WebApi, OpenId

10
  • Maybe in the InnerException, you can see th line number of SetResponseBody that throw the error. Commented Apr 2, 2021 at 7:03
  • Why are you using the low-level HttpResponse class instead of returning an ActionResult? Your SetResponseBody method does the same thing return Ok(new {message}) or return Ok(new {Message=message}) would do, only worse - you can test an ActionResult result returned by an action. You can't test an HttpResponse Commented Apr 2, 2021 at 12:35
  • PS that responseBody.CopyToAsync(originalResponseBody) results in an invalid JSON string. So, what are you trying to do here? Commented Apr 2, 2021 at 12:37
  • 1
    An alternative to what? You didn't post anything relevant to OpenID, which is your real question. You didn't even mention the stack you use - Web API? Razor Pages? In both cases, if your code returns the response, you're free to return whatever you want. If you use a controller and want to return a 401 with a message, all you need to do is return Unauthorized(new {message}) if not return Unauthorized(message);. Commented Apr 2, 2021 at 12:51
  • 1
    For Web API/MVC check the ControllerBase.Unauthorized method. For Razor Pages check PageModel.Unauthorized. If the response is returned by the provider though, you need to configure the provider Commented Apr 2, 2021 at 12:52

2 Answers 2

1

There are too many internals that need to be configured for the abstract HttpResponse to work as intended when mocking it.

I would suggest using DefaultHttpContext and extracting the default response created within that context.

[TestMethod]
public async Task SetResponseBody_TestMessageAsync() {
    //Arrange
    string expected = "TestMessage";
    string actual = null;
    HttpContext httpContext = new DefaultHttpContext();
    HttpResponse response = httpContext.Response
    
    //Act
    await ResponseRewriter.SetResponseBody(response, expected);
    
    //Assert
    //...
}

for the assertion, extract the content of the response body and assert its expected behavior.

Sign up to request clarification or add additional context in comments.

Comments

0

We can't write unit test for all method especially system library. so solution is make virtual function and use below method instead of direct await response.WriteAsync(json);

public virtual async Task  WriteAsync(string json)
{
    await response.WriteAsync(json);
}

and then

yourClassObject_Where_SetResponseBody_Exist.Setup(m => m.WriteAsync
       (It.IsAny<string>()));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.