Search code examples
c#unit-testingexceptionmoqxunit

Why is my global error handling not triggered when I throw a mock exception in my c# unit test?


I'm learning on how to write unit tests. From what I gathered, if I want to unit test a controller in a C# API, I need to mock/fake the inputs, the outputs and all dependencies of it. Next, I want to see what happens when my controller encounters an exception in its logic, so I fake an exception like below code

The unit test

        [Fact]
        public async Task GetCustomerById_FailedByDbOps()
        {
            // Arrange
            var customerId = 1;
            _customerRepoMock
                .Setup(repo => repo.GetOne(customerId))
                .ThrowsAsync(new Exception("Wrong or missing DB conn string"));

            // Act
            var result = await _controller.RetrieveCustomerById(customerId);

            // Assert
            var objResult = Assert.IsType<ObjectResult>(result);
            Assert.Equal(500, objResult.StatusCode);
        }

And I've already registered a global exception handler for my app

builder.Services.AddExceptionHandler<MyGlobalExceptionHandler>();

I expect the test to pass, as the global exception handler should be triggered, and return a custom error object based on the logic in MyGlobalExceptionHandler. But instead, the test failed with the mock exception itself.

enter image description here

Why is that? Did I do something wrong here? Or is it that a global exception handler can’t be triggered in unit tests? It works fine when I run the application locally.


Solution

  • Exception handling middleware will handle exceptions only after the controller will throw it (note that not only builder.Services.AddExceptionHandler... should be called but as far as I can see - app.UseExceptionHandler... too). So your controller still throws the exception in RetrieveCustomerById which will be handled later.

    There are two ways to go from here - either verify the exception with Assert.Throws(Async) instead of expecting the status code or go with the integration testing ASP.NET Core approach where you will start up the whole application and test the whole pipeline (you still can mock the dependencies here).

    See also: