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

Popular posts from this blog

Debugging Lua scripts in VS Code using MoonSharp

Creating a basic MVC 6 web application with Entity Framework 7 and xUnit - Part 2