How to Unit Test ASP .NET Core Middleware ?

Middleware plays a crucial role in the request processing pipeline of an ASP.NET Core application. It provides a way to handle cross-cutting concerns, such as logging, exception handling, and authentication, among others. To ensure the reliability and correctness of your middleware components, it’s important to write comprehensive unit tests. In this article, we’ll explore how to write unit tests for middleware in ASP.NET Core using a practical example.

Example Middleware: ExceptionHandlerMiddleware

Let’s consider a sample middleware called ExceptionHandlerMiddleware that catches exceptions and returns an error response to the client. This middleware provides a centralized mechanism to handle exceptions and maintain a consistent error response format. Here’s the implementation of the ExceptionHandlerMiddleware:

// ExceptionHandlerMiddleware.cs

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Net;
using System.Threading.Tasks;

namespace YourNamespace
{
    public class ExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public ExceptionHandlerMiddleware(RequestDelegate next, ILogger<ExceptionHandlerMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                _logger.LogError($"Something went wrong: {ex}");

                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                context.Response.ContentType = "application/json";

                await context.Response.WriteAsync("Internal Server Error.");
            }
        }
    }
}

The ExceptionHandlerMiddleware catches exceptions thrown by subsequent middleware or the request handler and returns an error response with a 500 status code and a plain text message.

Writing Unit Tests for ExceptionHandlerMiddleware

To ensure the correctness of the ExceptionHandlerMiddleware, we need to write unit tests that cover both the exception handling scenario and the scenario where no exception is thrown. Let’s explore how to write these unit tests using the Xunit testing framework.

 [Fact]
    public async Task Invoke_ExceptionCaught_ReturnsInternalServerError()
    {
        // Arrange
        var context = new DefaultHttpContext();
        context.Response.Body = new MemoryStream();
        context.Request.Path = "/";

        var expectedOutput = "Internal Server Error.";

        var middleware = new ExceptionHandlerMiddleware((innerHttpContext) =>
        {
            throw new Exception("Something went wrong");
        }, Mock.Of<ILogger<ExceptionHandlerMiddleware>>());

        // Act
        await middleware.Invoke(context);

        // Assert
        context.Response.Body.Seek(0, SeekOrigin.Begin);
        var body = await new StreamReader(context.Response.Body).ReadToEndAsync();
        Assert.Contains(expectedOutput, body);
        Assert.Equal(StatusCodes.Status500InternalServerError, context.Response.StatusCode);
        Assert.Equal("application/json", context.Response.ContentType);
    }

In this unit test:

  1. [Fact]: The [Fact] attribute indicates that this is a unit test method.

  2. Invoke_ExceptionCaught_ReturnsInternalServerError(): The method represents a test case where an exception is thrown by the middleware.

  3. Arrange:

    • We create an instance of DefaultHttpContext to simulate an HTTP context for the test.
    • The response body of the context is set to a MemoryStream to capture the response content.
    • The context.Request.Path is set to "/" to simulate the requested path.
    • The expectedOutput variable holds the expected error message that should be returned by the middleware.
    • We create an instance of the ExceptionHandlerMiddleware, passing in a delegate that throws an exception and a mock logger.
  4. Act:

    • The await middleware.Invoke(context) line invokes the middleware, simulating the request processing.
  5. Assert:

    • We reset the position of the response body stream to the beginning to read the response content.
    • The StreamReader is used to read the response body into a string variable named body.
    • Assert.Contains(expectedOutput, body) verifies that the expected error message is present in the response body.
    • Assert.Equal(StatusCodes.Status500InternalServerError, context.Response.StatusCode) ensures that the response status code is set to 500 (Internal Server Error).
    • Assert.Equal("application/json", context.Response.ContentType) verifies that the response content type is set to “application/json”.

In summary, this unit test sets up the necessary context for the middleware, triggers the middleware invocation, and then verifies the expected behavior of the middleware by examining the response.

Next Post Previous Post
No Comment
Add Comment
comment url