diff --git a/Changelog.md b/Changelog.md index 3a101a7..815ba44 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/Codenizer.HttpClient.Testable.sln b/Codenizer.HttpClient.Testable.sln index 8eaf764..bea189c 100644 --- a/Codenizer.HttpClient.Testable.sln +++ b/Codenizer.HttpClient.Testable.sln @@ -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 @@ -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 diff --git a/Directory.Build.props b/Directory.Build.props index 6c4b50d..8b41300 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@ - 2.6.0.0 + 2.7.0.0 Sander van Vliet Codenizer BV - 2023 Sander van Vliet + 2024 Sander van Vliet 10 diff --git a/src/Codenizer.HttpClient.Testable/TestableHttpClientFactory.cs b/src/Codenizer.HttpClient.Testable/TestableHttpClientFactory.cs index 20c7d0e..3d46b7b 100644 --- a/src/Codenizer.HttpClient.Testable/TestableHttpClientFactory.cs +++ b/src/Codenizer.HttpClient.Testable/TestableHttpClientFactory.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Net.Http; namespace Codenizer.HttpClient.Testable @@ -8,21 +9,33 @@ namespace Codenizer.HttpClient.Testable /// public class TestableHttpClientFactory : IHttpClientFactory { - private readonly Dictionary _clientConfigurations = new Dictionary(); + private readonly Dictionary _namedHandlers = new(); + private readonly Dictionary> _clientConfigurations = new(); /// 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; } /// @@ -32,7 +45,21 @@ public System.Net.Http.HttpClient CreateClient(string name) /// The instance that should be used for the client name public void ConfigureClient(string name, TestableMessageHandler messageHandler) { - _clientConfigurations.Add(name, messageHandler); + _namedHandlers.Add(name, messageHandler); + } + + /// + /// Configure a client name to return a with a and configure the instance with defaults + /// + /// The name of the as requested by + /// A lambda to configure the created + /// The instance that will be used for the client name + public TestableMessageHandler ConfigureClient(string name, Action configure) + { + _clientConfigurations.Add(name, configure); + var handler = ConfigureClient(name); + + return handler; } /// @@ -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; } } diff --git a/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenConfiguringHandlerThroughHttpClientFactory.cs b/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenConfiguringHandlerThroughHttpClientFactory.cs new file mode 100644 index 0000000..6041246 --- /dev/null +++ b/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenConfiguringHandlerThroughHttpClientFactory.cs @@ -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; + } + } +}