Skip to content

Commit

Permalink
Add option to configure the created HttpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
sandermvanvliet committed Mar 26, 2024
1 parent 5cb46e9 commit f053107
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 11 deletions.
12 changes: 12 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Codenizer.HttpClient.Testable Changelog

## 2.7.0.0

Add the option to specify a lambda to configure the `HttpClient` as it's being created by the `TestableHttpClientFactory`.

For example:

```csharp
_factory.ConfigureClient(
"name of client",
httpClient => httpClient.BaseAddress = new Uri("https://example.com"));
```

## 2.6.0.0

Downgrade Microsoft.Extensions.Http from 7.0.0 to 6.0.0 for greater compatibility.
Expand Down
5 changes: 3 additions & 2 deletions Codenizer.HttpClient.Testable.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
# Visual Studio Version 17
VisualStudioVersion = 17.8.34408.163
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C0192DBD-4673-4573-BF61-FD0B4EFB7D11}"
EndProject
Expand All @@ -14,6 +14,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{61B5BDEC-5FE6-4196-A6A0-566E9012E2A9}"
ProjectSection(SolutionItems) = preProject
Changelog.md = Changelog.md
Directory.Build.props = Directory.Build.props
README.md = README.md
EndProjectSection
EndProject
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

<Project>
<PropertyGroup>
<Version>2.6.0.0</Version>
<Version>2.7.0.0</Version>
<Authors>Sander van Vliet</Authors>
<Company>Codenizer BV</Company>
<Copyright>2023 Sander van Vliet</Copyright>
<Copyright>2024 Sander van Vliet</Copyright>
</PropertyGroup>
<PropertyGroup>
<LangVersion>10</LangVersion>
Expand Down
41 changes: 34 additions & 7 deletions src/Codenizer.HttpClient.Testable/TestableHttpClientFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Net.Http;

namespace Codenizer.HttpClient.Testable
Expand All @@ -8,21 +9,33 @@ namespace Codenizer.HttpClient.Testable
/// </summary>
public class TestableHttpClientFactory : IHttpClientFactory
{
private readonly Dictionary<string, TestableMessageHandler> _clientConfigurations = new Dictionary<string, TestableMessageHandler>();
private readonly Dictionary<string, TestableMessageHandler> _namedHandlers = new();
private readonly Dictionary<string, Action<System.Net.Http.HttpClient>> _clientConfigurations = new();

/// <inheritdoc cref="IHttpClientFactory.CreateClient"/>
public System.Net.Http.HttpClient CreateClient(string name)
{
if (!_clientConfigurations.ContainsKey(name))
System.Net.Http.HttpClient httpClient;

if (!_namedHandlers.ContainsKey(name))
{
// No client was configured but IHttpClientFactory.CreateClient
// requires us to return a new HttpClient instance. So we will
// return one but it's going to be backed by a default
// TestableMessageHandler.
return new System.Net.Http.HttpClient(new TestableMessageHandler());
httpClient = new System.Net.Http.HttpClient(new TestableMessageHandler());
}
else
{
httpClient = new System.Net.Http.HttpClient(_namedHandlers[name]);
}

return new System.Net.Http.HttpClient(_clientConfigurations[name]);
if (_clientConfigurations.ContainsKey(name))
{
_clientConfigurations[name](httpClient);
}

return httpClient;
}

/// <summary>
Expand All @@ -32,7 +45,21 @@ public System.Net.Http.HttpClient CreateClient(string name)
/// <param name="messageHandler">The <see cref="TestableMessageHandler"/> instance that should be used for the client name</param>
public void ConfigureClient(string name, TestableMessageHandler messageHandler)
{
_clientConfigurations.Add(name, messageHandler);
_namedHandlers.Add(name, messageHandler);
}

/// <summary>
/// Configure a client name to return a <see cref="HttpClient"/> with a <see cref="TestableMessageHandler"/> and configure the <see cref="HttpClient"/> instance with defaults
/// </summary>
/// <param name="name">The name of the <see cref="HttpClient"/> as requested by <see cref="CreateClient"/></param>
/// <param name="configure">A lambda to configure the created <see cref="HttpClient"/></param>
/// <returns>The <see cref="TestableMessageHandler"/> instance that will be used for the client name</returns>
public TestableMessageHandler ConfigureClient(string name, Action<System.Net.Http.HttpClient> configure)
{
_clientConfigurations.Add(name, configure);
var handler = ConfigureClient(name);

return handler;
}

/// <summary>
Expand All @@ -43,7 +70,7 @@ public void ConfigureClient(string name, TestableMessageHandler messageHandler)
public TestableMessageHandler ConfigureClient(string name)
{
var messageHandler = new TestableMessageHandler();
_clientConfigurations.Add(name, messageHandler);
_namedHandlers.Add(name, messageHandler);
return messageHandler;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Net.Http;
using System.Reflection;
using FluentAssertions;
using Xunit;

namespace Codenizer.HttpClient.Testable.Tests.Unit
{
public class WhenConfiguringHandlerThroughHttpClientFactory
{
private readonly TestableHttpClientFactory _factory = new();
private const string ClientName = "test client name";

[Fact]
public void GivenClientNameWasNotConfigured_NewHandlerIsRegistered()
{
var client = _factory.CreateClient(ClientName);

client
.Should()
.NotBeNull();
}

[Fact]
public void GivenClientNameWasConfigured_ClientUsesConfiguredHandler()
{
var handler = _factory.ConfigureClient(ClientName);

var client = _factory.CreateClient(ClientName);

TheHandlerOf(client)
.Should()
.Be(handler);
}

[Fact]
public void GivenClientWasConfiguredWithSpecificHandler_ClientIsUsingSpecifiedHandler()
{
var handler = new TestableMessageHandler();
_factory.ConfigureClient(ClientName, handler);

var client = _factory.CreateClient(ClientName);

TheHandlerOf(client)
.Should()
.Be(handler);
}

[Fact]
public void GivenClientNameWasConfiguredWithClientConfiguration_ConfigurationIsAppliedToClient()
{
_factory.ConfigureClient(ClientName, client => client.BaseAddress = new Uri("https://example.com"));

var client = _factory.CreateClient(ClientName);

client
.BaseAddress
.Should()
.Be(new Uri("https://example.com"));
}

private HttpMessageHandler? TheHandlerOf(System.Net.Http.HttpClient client)
{
var field = typeof(HttpMessageInvoker).GetField("_handler", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);

return field?.GetValue(client) as HttpMessageHandler;
}
}
}

0 comments on commit f053107

Please sign in to comment.