Testing the response body of middleware in ASP.NET Core
Testing custom middleware in ASP.NET Core is very easy (e.g. for the response status code), but it’s a bit tricky if you want to test the response body because 
DefaultHttpContext uses a Null-Stream for the body and thus is always empty. Creating an implementation of HttpContext is way too much overhead, but you can use a custom Stream object for the response body of DefaultHttpContext. Here is how you do it.   
// use this memory stream for the response body
var responseBodyStream = new MemoryStream();
var context = new DefaultHttpContext();
context.Features.Get<IHttpResponseFeature>().Body = responseBodyStream;
var middleware = new MyMiddleware(async (c) => { await Task.Delay(0); });
await middleware.Invoke(context);
// reset the position
responseBodyStream.Position = 0;
// read the body
string bodyContent = new StreamReader(context.Response.Body).ReadToEnd();   
The trick is to access the IHttpResponseFeature of the DefaultHttpContext and to use a custom MemoryStream. The memory stream can be read after invoking the middleware, you just need to set the position back to zero. Here is a full example of a custom middleware with tests: 
// custom middleware - returns "Hello World !!!" for a specific header
public class HelloWorldMiddleware
{
    private readonly RequestDelegate _next;
    public HelloWorldMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        bool contains = context.Request.Headers.TryGetValue(
                           "HelloWorld", out StringValues values);
        // if header contains HelloWorld=true, return custom response
        if (contains && values == "true")
        {
            var result = "Hello World !!!";
            await context.Response.WriteAsync(result);
        }
        else 
        {
            await _next(context);
        }
    }
}
// the test class - make sure the correct response is returned
// for the "HelloWorld" header, but does nothing for everything else
public class HelloWorldMiddlewareTest
{
   [Fact]
   public async Task Header_WhenContainsHelloWorld_ResponseBodyIsHelloWorld()
   {
      var responseBodyStream = new MemoryStream();
      var nextDelegateCalled = false;
      var context = new DefaultHttpContext();
      context.Request.Headers.Add("HelloWorld", "true");
      // use a custom stream for the response body
      context.Features.Get<IHttpResponseFeature>().Body = responseBodyStream;
      var middleware = new HelloWorldMiddleware(
         async (c) => 
         { await Task.Delay(0); nextDelegateCalled = true; });
      await middleware.Invoke(context);
      // reset position before reading
      responseBodyStream.Position = 0;
      string bodyContent = new StreamReader(context.Response.Body).ReadToEnd();
      // _next should not be called
      Assert.False(nextDelegateCalled);
      // and the body should be:
      Assert.Equal("Hello World !!!", bodyContent);
   }
   [Fact]
   public async Task Header_WhenNotContainsHelloWorld_CallsNextDelegate()
   {
      var responseBodyStream = new MemoryStream();
      var nextDelegateCalled = false;
      var context = new DefaultHttpContext();
      context.Features.Get<IHttpResponseFeature>().Body = responseBodyStream;
      var middleware = new HelloWorldMiddleware(
         async (c) => 
         { await Task.Delay(0); nextDelegateCalled = true; });
      await middleware.Invoke(context);
      responseBodyStream.Position = 0;
      string bodyContent = new StreamReader(context.Response.Body).ReadToEnd();
      // _next delegate must be called
      Assert.True(nextDelegateCalled);
      Assert.NotEqual("Hello world !!!", bodyContent);
   }
}  
 
Comments
Post a Comment