Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inspect DefaultHttpContext body in unit test situation

I'm trying to use the DefaultHttpContext object to unit test my exception handling middleware.

My test method looks like this:

[Fact]
public async Task Invoke_ProductionNonSuredException_ReturnsProductionRequestError()
{
    var logger = new Mock<ILogger<ExceptionHandlerMiddleware>>();
    var middleWare = new ExceptionHandlerMiddleware(next: async (innerHttpContext) =>
    {
        await Task.Run(() =>
        {
            throw new Exception();
        });
    }, logger: logger.Object);

    var mockEnv = new Mock<IHostingEnvironment>();
    mockEnv.Setup(u => u.EnvironmentName).Returns("Production");

    var context = new DefaultHttpContext();

    await middleWare.Invoke(context, mockEnv.Object);

    var reader = new StreamReader(context.Response.Body);
    var streamText = reader.ReadToEnd();

    //TODO: write assert that checks streamtext is the expected production return type and not the verbose development environment version.
}

In my middleware, I am writing to the context like this:

public static Task WriteResponse(HttpContext context, HttpStatusCode statusCode, object responseData, Formatting jsonFormatting)
{
    context.Response.ContentType = "application/json";
    context.Response.StatusCode = (int)statusCode;
    return context.Response.WriteAsync(JsonConvert.SerializeObject(responseData, jsonFormatting));
}

To give you more insight into the middleware approach I've taken, I'm taking the approach found in this answer here.

Works fine when the app is running it's normal pipeline. However, using the DefaultHttpContext method in the test, the response body always comes back empty, and ContentLength is null. Thus, my streamText variable in the test is an empty string.

Is it possible to inspect what the middleware is writing to the context in this situation? Is this the appropriate way, or is there a better way.

like image 624
Chris Avatar asked Aug 30 '17 11:08

Chris


1 Answers

Consider setting the body yourself so that you have control of the stream

Comment provided by @AndrewStanton-Nurse

The Response.Body stream in DefaultHttpContext is Stream.Null, which is a stream that ignores all reads/writes. You need to set the Stream yourself before calling the method.

Further - This will now allow the body to be set, but in order to read it correctly we must set the pointer to the beginning as per this answer, before using the StreamReader.

//...code removed for brevity

var context = new DefaultHttpContext();
context.Response.Body = new MemoryStream();

await middleWare.Invoke(context, mockEnv.Object);


context.Response.Body.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(context.Response.Body);
var streamText = reader.ReadToEnd();

//...code removed for brevity
like image 167
Nkosi Avatar answered Oct 02 '22 06:10

Nkosi