A fluent API library for mocking HttpClient and HttpClientFactory for testing
A clean way to set up your HTTP mocks
I started the TeePee (The name is based on the last two characters of HTTP) library a couple of years ago and it has since become battle tested and proven to work quite nicely.
It’s quite common to need to mock your HTTP responses and confirm that you’re sending the expected data to the correct place. This can be quite painful to set up, especially when using HttpClientFactory and the various ways you can inject HttpClients.
All of the various ways you can use it are documented in the Readme, including both a) if you cover DI in behavioural tests or b) if you want to manually inject HttpClient/HttpClientFactory into your SUT in a unit test.
There’s also an extension Package for Refit. Why wouldn’t I just mock the refit interface I hear you say? Well you might decide that’s enough for your situation, but it won’t mean that all the setup code in your startup is covered by the tests - such as Base URI and any Http Handlers which often configure authentication for third party APIs.
Manual Injection example
Here’s an example of mocking a GET request and using a simple manual injection of a Named HttpClient:
// Subject Under Test publicclassToTest { publicToTest(HttpClientFactory httpClientFactory) { var httpClient = httpClientFactory.CreateClient("MyHttpClient"); // ... etc. } }
// Test Code var builder = new TeePeeBuilder("MyHttpClient"); builder.ForRequest("https://some.api/path/resource", HttpMethod.Post) .ThatHasBody(new { Value = 12 }) .ThatContainsQueryParam("filter", "those") .ThatContainsHeader("ApiKey", "123abc-xyz987") .Responds() .WithStatus(HttpStatusCode.Created) .WithBody(new { Id = Guid.NewGuid() });
var httpClientFactory = (await builder.Build()) .Manual("https://some.api") .CreateHttpClientFactory(); /* Sadly, if your production code DI registers the BaseURL then you have to duplicate that in the test, passing it to .Manual(); no coverage with manual injection like this. */
var sut = new ToTest(httpClientFactory); // ... etc.
Dependency Injection example
And here’s an example of using Refit and also covering startup registrations in the test:
publicstaticclassGitHubApiStartupExtensions { // Extension called from Startup class in production code publicstaticvoidAddGitHubApi(this IServiceCollection services) { services.AddRefitClient<IApiService>() .ConfigureHttpClient(c => c.BaseAddress = new("https://api.github.com")); } }
publicasync Task<string> Greeting(string userId) { var name = await _apiService.GetUser(userId); return$"Hello, {name}"; } }
/* --- Tests --- */
// In reality, this wouldn't be in the test, you would call production code to setup registrations var services = new ServiceCollection(); services.AddGitHubApi(); services.AddTransient<SomeLogic>();
// Test setup var builder = new TeePeeBuilder(); builder.ForRequest("https://api.github.com/users/abc-123", HttpMethod.Get) .Responds() .WithBody(new { Name = "User's Name" }) .WithStatus(HttpStatusCode.OK);
// Simulate Production Code - you'd probably be testing some edge API not just a class like this var greeting = await services.BuildServiceProvider().GetRequiredService<SomeLogic>().Greeting("abc-123");
Assert.Equal("Hello, User's Name", user.Name);
Feedback
All the documentation is in the Readme so I would start there. There are also Examples.
Any feedback or issues welcome, either via GitHub or Twitter.