Handling Background Worker Unit Tests in ASP.NET

Matt Ghafouri
3 min readOct 14, 2024

--

writing Background worker service unit test

Introduction

Background services are a powerful feature in ASP.NET Core, allowing you to run long-running tasks in the background. However, testing these services can be challenging due to their asynchronous and indefinite nature. In this article, we’ll explore how to create a base background worker, extend it for specific tasks, and effectively unit test these background services.

Creating a Base Background Worker

First, let’s create a base class for our background worker. This base class will handle common functionalities such as logging the start and stop events and managing the execution period and last execution time.

Extending the Base Worker

Next, let’s create a specific background worker by extending the base class. This worker will perform a specific task, such as logging cache status.

Unit Testing the Background Worker

Testing background services can be tricky due to their asynchronous nature. Here’s how you can effectively test that your background worker is functioning correctly.

  1. Use a Shorter Execution Period for Testing: Override the ExecutionPeriod property to a shorter duration specifically for testing purposes.
  2. UseManualResetEventSlimfor Synchronization: Use a ManualResetEventSlim to wait for the background task to complete before asserting.
  3. Ensure Proper Cancellation Token Handling: Make sure the cancellation token is properly handled to stop the background service after the test.

Here’s the complete test class:

Purpose of the Callback and ManualResetEventSlim :

  1. Synchronization: The callback is used to signal that the LogCacheStatusAsyncmethod has been called. This is important because StartAsyncis likely starting some background work, and you need to ensure that this work has progressed to the point where LogCacheStatusAsync has been invoked before you make your assertions.
  2. Waiting for Completion: The ManualResetEventSlim is used to wait for the signal from the callback. This ensures that the test does not proceed to the assertions until the LogCacheStatusAsyncmethod has been called.

Why Not Just Use await ?

In this specific test,awaitalone is not sufficient becauseStartAsynclikely starts a background task that runs independently of the main test thread. The test needs a way to know when the background task has reached the point where it callsLogCacheStatusAsync. The callback provides this notification.

You might say, “I removed ManualResetEventSlim, but my worker unit tests still pass.” That could be true because your worker’s execution time is short enough. So, before the test asserts whether the method was invoked or not, your code is still able to call LogCacheStatusAsync in time.

But if you add a manual delay inside your worker ExecuteAsync method, we can be sure that the worker will take more time to finish, then your unit tests will fail.

Just to have more reliable tests, better to use MnaualResetEventSlim. By the way, let me know if you find a better approach, and then I will refactor my code to follow the better one.

I have no bias regarding my code, even though the code from yesterday can be considered legacy.😉🍩

Conclusion

Testing background services in ASP.NET Core can be challenging due to their asynchronous and indefinite nature. By creating a base worker class, extending it for specific tasks, and using synchronization techniques in your tests, you can effectively ensure that your background services are functioning correctly. This approach provides a robust framework for managing and testing background tasks in your applications.

I’ll be happy to have you on my LinkedIn Network: My LinkedIn 😎

Cheers … Matt Ghafouri

--

--

Matt Ghafouri
Matt Ghafouri

Written by Matt Ghafouri

Software Engineer | Sharing My Journey in Tech & Startups, Story Teller