简体   繁体   中英

How do I test the integration between the Global error handling middleware and my controller in ASP.Net Core?

I'm trying to write some test with XUnit, specifically I'd like to have a test that ensures that when a certain exception is thrown it gets remapped into a meaningful error code. I already set up the Global error handling middleware and it works correctly. Here there is some example code of how my solution works:

My controller with a post endpoint that can return 200 or 404

//Controller
[HttpPost]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<StatusCodeResult> Create([FromBody] Request request) {
    //Process request
    handler.Handle(request);
    return Ok();
}

The Middleware for the Global error handling that remaps exceptions into Error codes

//StartUp Middleware
app.UseExceptionHandler(builder => {
    builder.Run(handler: async context => {
        IExceptionHandlerFeature error = context.Features.Get<IExceptionHandlerFeature>();
        if (error != null) {
            int statusCode = (int)GetStatusCodeForException(error.Error);
            context.Response.StatusCode = statusCode;
            context.Response.ContentType = "application/json";

            await context.Response.WriteAsync(new ErrorDetails { StatusCode = statusCode, Message = error.Error.Message }.ToString());
        }
    });
});

And then my test in where I arrange some mocks, instantiate the controller and call the Create method

//UnitTest
[Fact]
public async Task Test()
{
    //Arrange
    var mockHandler = new Mock<IHandler>();
    mockHandler.Setup(handler => handler.Handle(It.IsAny<Request>())).Throws(new CustomException(It.IsAny<string>()));

    MyController myController = new MyController();

    //Act
    var statusCodeResult = await myController.Create(request);

    //Assert
    StatusCodeResult result = Assert.IsType<NotFoundResult>(statusCodeResult);
}

Here I want to ensure that the CustomException is remapped into a 404 status code. How do I do it? Any help is appreciated.

In your test the middleware is not available. You need to spin up a hosting environment to do that, the package Microsoft.AspNetCore.TestHost provides you with one that you can use for testing:

[Fact]
public async Task Test1()
{
    using var host = new TestServer(Program.CreateHostBuilder(null));
    var client = host.CreateClient();

    var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/controller");

    var result = await client.SendAsync(requestMessage);

    var status = result.StatusCode;

    // TODO: assertions
}

Now when you call your API in a way an exception is thrown, the middleware should be executed and covered.

You can use the WebApplicationFactory class from the Microsoft.AspNetCore.Mvc.Testing nuget package. This bootstraps your application in-memory to allow for end to end functional tests. Rather than calling individual action methods you can make calls via HttpClient to ensure all middleware etc is called.

You use the Startup class that you have already defined in your main entry project and can add mock/fake objects to the IoC container as required. This allows you to verify/setup any dependencies.

You can then inject this as an IClassFixture . Once injected calling .CreateClient() on the instance will return an HttpClient , through which you can make requests.

Here is an example implementation:

// app factory instance
public class TestAppFactory : WebApplicationFactory<Startup>
{
    // mock for setup/verify
    public Mock<IHandler> MockHandler { get; } = new Mock<IHandler>();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            services.AddSingleton(MockHandler);
        });
    }
}

public class MyTestClass : IClassFixture<TestAppFactory>
{
    private readonly TestAppFactory _factory;
    private readonly HttpClient _client;
    private readonly Mock<IHandler> _mockHandler;

    public MyTestClass(TestAppFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
        _mockHandler = factory.MockHandler;
    }

    [Fact]
    public async Task Test()
    {
        // make calls via _client
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM