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.
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.
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: