Flakiness in unit tests refers to the tendency of a test to sometimes pass and sometimes fail, even when the code has not changed. The result is an unreliable test that cannot be trusted to accurately reflect the behavior of the code.
Flaky tests make it hard to tell whether a failure reflects a real bug or just an unstable test. Developers waste time diagnosing problems that do not exist, confidence in the test suite erodes, and teams become reluctant to refactor code for fear of triggering false failures.
Tests that depend on external systems such as databases or web services are a common source of flakiness, especially when those systems are unavailable or behave inconsistently between runs. What counts as "external" depends on context: a database running on the same machine may be acceptable, while one running on a remote server is clearly external. The same reasoning applies to web services.
This post describes how to prevent HTTP requests to external services in .NET unit tests, helping you catch and fix these dependencies early to build a more reliable test suite.
#Code
I've already written about observing all http requests in a .NET application. That post covers different ways to observe HTTP requests in a .NET application, whether from the current process or an external one. For unit tests, you only need to detect requests from the current process, so using DiagnosticListener is the simplest approach and provides more contextual data if needed.
Next, you need to register the DiagnosticListener to start listening for HTTP requests before the first test runs. You could use your test framework's setup hooks for this, but C# provides a built-in way to execute a static method when an assembly is loaded, making the solution test-framework agnostic. The post "Executing code before Main in .NET" explains how to declare a ModuleInitializer to run code when the assembly loads.
Let's combine both techniques to detect and prevent HTTP requests to external services in unit tests!
HttpRequestsDetector.cs (C#)
using System.Diagnostics;
using System.Runtime.CompilerServices;
internal static class HttpRequestsDetector
{
// Configure the list of allowed domains
private static readonly HashSet<string> AllowedHosts = new(StringComparer.OrdinalIgnoreCase)
{
"localhost",
};
// Methods decorated with [ModuleInitializer] are executed when the DLL is loaded,
// so before the first test is executed. This is test-framework agnostic!
// No need to read the documentation of your favorite test framework :)
[ModuleInitializer]
public static void Initialize()
{
var eventListener = new DiagnosticSourceSubscriber();
eventListener.Subscribe();
}
// Register a DiagnosticListener to listen to all http requests.
// When a request is sent to an external service, an exception is thrown and the test fails.
private sealed class DiagnosticSourceSubscriber : IDisposable, IObserver<DiagnosticListener>
{
private IDisposable? _allSourcesSubscription;
private IDisposable? _subscription;
public void Subscribe()
{
// Register the listener
_allSourcesSubscription ??= DiagnosticListener.AllListeners.Subscribe(this);
}
public void OnNext(DiagnosticListener value)
{
if (value.Name == "HttpHandlerDiagnosticListener")
{
_subscription = value.Subscribe(new HttpHandlerDiagnosticListener());
}
}
public void OnCompleted() { }
public void OnError(Exception error) { }
public void Dispose()
{
_subscription?.Dispose();
_subscription = null;
_allSourcesSubscription?.Dispose();
_allSourcesSubscription = null;
}
private sealed class HttpHandlerDiagnosticListener : IObserver<KeyValuePair<string, object?>>
{
private static readonly Func<object, HttpRequestMessage?> GetRequestPropertyValue = CreateGetRequestPropertyValue();
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(KeyValuePair<string, object?> value)
{
if (value.Key is "System.Net.Http.Request")
{
var request = GetRequestPropertyValue(value.Value!);
if (request?.RequestUri?.Host is not string host)
return;
if (!AllowedHosts.Contains(host))
throw new InvalidOperationException($"Requesting external resource '{request.RequestUri}' from unit tests is forbidden");
}
}
// The object sent by .NET to the listener is not public, so you need to
// use reflection to get it.
private static Func<object, HttpRequestMessage?> CreateGetRequestPropertyValue()
{
var requestDataType = Type.GetType("System.Net.Http.DiagnosticsHandler+RequestData, System.Net.Http", throwOnError: true);
var requestProperty = requestDataType!.GetProperty("Request");
return (object o) => (HttpRequestMessage?)requestProperty!.GetValue(o);
}
}
}
}
If a test requests an external service, you will get an exception like this:

#Additional resources
Do you have a question or a suggestion about this post? Contact me!