ASP.NET Core 2.1 middlewares part 2: Unit test a custom middleware

 

How to unit test a middleware ?

It’s very easy to unit test a middleware in ASP.NET Core.

There three important things to understand here.

First the Invoke method takes in parameter an HttpContext instance.

We all know how it’s painful to mock an HttpContext… We don’t need! ASP.NET Core provide a “fake” HttpContext named DefaultHttpContext. Feeling better now ? 🙂

Second thing to know, if you need to unit test the Response body property you have to initialize it tourslef like this:

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

and just before reading the content, reset the body’s stream position back to 0.

context.Response.Body.Seek(0, SeekOrigin.Begin);

And finally, the RequestDelegate object passed in parameter of the middleware’s constructor is simply a delegate, Action exactly.

We are ready to unit test our custom middleware

In these scenarii, we will use XUnit and FluentAssertion

Case 1: scenario with a custom error:

public class CustomExceptionMiddlewareTests
{
   [Fact]
   public async Task WhenACustomExceptionIsRaised_CustomExceptionMiddlewareShouldHandleItToCustomErrorResponseAndCorrectHttpStatus()
   {
      // Arrange
      var middleware = new CustomExceptionMiddleware((innerHttpContext) =>
      {
         throw new NotFoundCustomException("Test", "Test");
      });

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

      //Act
      await middleware.Invoke(context);

      context.Response.Body.Seek(0, SeekOrigin.Begin);
      var reader = new StreamReader(context.Response.Body);
      var streamText = reader.ReadToEnd();
      var objResponse = JsonConvert.DeserializeObject<CustomErrorResponse>(streamText);

      //Assert
      objResponse
      .Should()
      .BeEquivalentTo(new CustomErrorResponse { Message = "Test", Description = "Test" });

      context.Response.StatusCode
      .Should()
      .Be((int)HttpStatusCode.NotFound);
   }
}

Case 2: scenario with an unhandled error:

public class CustomExceptionMiddlewareTests
{
   [Fact]
   public async Task WhenAnUnExpectedExceptionIsRaised_CustomExceptionMiddlewareShouldHandleItToCustomErrorResponseAndInternalServerErrorHttpStatus()
   {
      // Arrange
      var middleware = new CustomExceptionMiddleware(next: (innerHttpContext) =>
      {
         throw new Exception("Test");
      });

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

      //Act
      await middleware.Invoke(context);

      context.Response.Body.Seek(0, SeekOrigin.Begin);
      var reader = new StreamReader(context.Response.Body);
      var streamText = reader.ReadToEnd();
      var objResponse = JsonConvert.DeserializeObject<CustomErrorResponse>(streamText);

     //Assert
     objResponse
     .Should()
     .BeEquivalentTo(new CustomErrorResponse { Message = "Unexpected error", Description = "Unexpected error" });

     context.Response.StatusCode
     .Should()
     .Be((int)HttpStatusCode.InternalServerError);
   }
}

 

Easy isn’t it ? 😉