The top level loop in a hosted service (worker) needs to execute while the CancellationToken
has not recieved a cancellation request. This needs to be wrapped in a generic try/catch as when a BackgroundService throws an unhandled exception, the exception is lost and the service appears unresponsive. This is because the base class BackgroundService
is awaiting the task to complete and return.
.NET 6 fixes this behavior by logging the exception and stopping the host.
As the code in this example was .NET 5 and I needed to test the try/catch is not removed.
1 | protected override async Task ExecuteAsync(CancellationToken cancellationToken) |
This code posed two problems
- How does the test break out the
while
loop - How do I test a
protected
method.
The second issue can be resolved by additional setup to expose protected
behaviour the first needed the token to be cancelled.
There were two ways this could be done:
- cancelling after a specific amount of time
- mocking a dependnacy and using
Callback
to call the.Cancel()
method on the token
Cancelling after a specific amount of time
This should work but the test could be flaky (fails to produce the same result each time)
1 | // Arrange |
Cancel callback
This will only be possible if you have some mocked dependency.
1 | // Arrange |
As the method handles the Exception
the assertion could be on the ILogger else the exception will need to be expected in the unit test, with xUnit this can be handled with Assert.ThrowsAsync.