Skip to content

Commit 447f8ed

Browse files
committed
Switch to MS http client factory in Dibix.Testing
1 parent be7d8ac commit 447f8ed

8 files changed

Lines changed: 102 additions & 38 deletions

File tree

src/Dibix.Sdk.CodeGeneration/Output/ApiClientImplementationWriter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ protected override void WriteController(CodeGenerationContext context, CSharpSta
2828
string interfaceName = $"I{className}";
2929
CSharpAnnotation interfaceDescriptor = new CSharpAnnotation("HttpService", new CSharpValue($"typeof({interfaceName})"));
3030
CSharpClass @class = output.AddClass(className, CSharpModifiers.Public | CSharpModifiers.Sealed, interfaceDescriptor)
31-
.Implements(interfaceName)
32-
.AddField("BaseAddress", nameof(Uri), new CSharpValue($"new {nameof(Uri)}(\"{context.Model.BaseUrl.TrimEnd('/')}/\")"), CSharpModifiers.Private | CSharpModifiers.Static | CSharpModifiers.ReadOnly);
31+
.Implements(interfaceName);
32+
33+
if (!context.Model.UseMicrosoftHttpClient)
34+
@class.AddField("BaseAddress", nameof(Uri), new CSharpValue($"new {nameof(Uri)}(\"{context.Model.BaseUrl.TrimEnd('/')}/\")"), CSharpModifiers.Private | CSharpModifiers.Static | CSharpModifiers.ReadOnly);
3335

3436
bool hasBodyParameter = controller.Actions.Any(x => x.RequestBody != null);
3537
bool requiresAuthorization = controller.Actions.Any(x => x.SecuritySchemes.HasEffectiveRequirements);

src/Dibix.Testing/Dibix.Testing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
3030
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
3131
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
32+
<PackageReference Include="Microsoft.Extensions.Http" />
3233
<PackageReference Include="MSTest.TestFramework" />
3334
<PackageReference Include="System.Diagnostics.EventLog" />
3435
</ItemGroup>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Net.Http;
3+
using Dibix.Http.Client;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using IHttpClientBuilder = Microsoft.Extensions.DependencyInjection.IHttpClientBuilder;
6+
using IHttpClientFactory = System.Net.Http.IHttpClientFactory;
7+
8+
namespace Dibix.Testing.Http
9+
{
10+
internal abstract class HttpClientFactoryBuilder
11+
{
12+
private Action<IHttpClientBuilder> _configure;
13+
14+
protected abstract string ClientName { get; }
15+
16+
public HttpClientFactoryBuilder Configure(Action<IHttpClientBuilder> configure)
17+
{
18+
_configure = configure;
19+
return this;
20+
}
21+
22+
public IHttpClientFactory Build()
23+
{
24+
IServiceCollection services = new ServiceCollection();
25+
26+
IHttpClientBuilder httpClientBuilder = services.AddHttpClient(ClientName, Configure);
27+
ConfigureHttpClientDefaults(httpClientBuilder);
28+
Configure(httpClientBuilder);
29+
_configure?.Invoke(httpClientBuilder);
30+
31+
ServiceProvider serviceProvider = services.BuildServiceProvider();
32+
IHttpClientFactory httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
33+
return httpClientFactory;
34+
}
35+
36+
protected virtual void Configure(IHttpClientBuilder builder) { }
37+
38+
protected virtual void Configure(HttpClient client) { }
39+
40+
private static void ConfigureHttpClientDefaults(IHttpClientBuilder httpClientBuilder)
41+
{
42+
AddHttpMessageHandler<FollowRedirectHttpMessageHandler>(httpClientBuilder);
43+
AddHttpMessageHandler<TraceProxyHttpMessageHandler>(httpClientBuilder);
44+
AddHttpMessageHandler<EnsureSuccessStatusCodeHttpMessageHandler>(httpClientBuilder);
45+
}
46+
47+
private static void AddHttpMessageHandler<T>(IHttpClientBuilder httpClientBuilder) where T : DelegatingHandler, new()
48+
{
49+
httpClientBuilder.AddHttpMessageHandler(() => new T());
50+
}
51+
}
52+
}

src/Dibix.Testing/Http/HttpServiceFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Reflection;
55
using Dibix.Http.Client;
6+
using IHttpClientFactory = System.Net.Http.IHttpClientFactory;
67

78
namespace Dibix.Testing.Http
89
{
@@ -20,7 +21,7 @@ public static TService CreateServiceInstance<TService>(IHttpClientFactory httpCl
2021

2122
private static TService CreateServiceInstance<TService>(params KeyValuePair<Type, object>[] args)
2223
{
23-
ICollection<KeyValuePair<Type, object>> normalizedArgs = args.Concat(EnumerableExtensions.Create(new KeyValuePair<Type, object>(typeof(string), TestHttpClientConfiguration.HttpClientName))).ToArray();
24+
ICollection<KeyValuePair<Type, object>> normalizedArgs = args.Concat(EnumerableExtensions.Create(new KeyValuePair<Type, object>(typeof(string), TestHttpClientFactoryBuilder.HttpClientName))).ToArray();
2425
Type contractType = typeof(TService);
2526
Type implementationType = ResolveImplementationType(contractType);
2627
Type[] constructorSignature = normalizedArgs.Select(x => x.Key).ToArray();
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Net.Http;
33
using Dibix.Http.Client;
4+
using IHttpClientFactory = System.Net.Http.IHttpClientFactory;
45

56
namespace Dibix.Testing.Http
67
{
@@ -10,7 +11,7 @@ public class HttpTestContext<TService> : HttpTestContext where TService : IHttpS
1011

1112
public HttpTestContext(TService service, IHttpClientFactory httpClientFactory, IHttpAuthorizationProvider httpAuthorizationProvider) : base(httpClientFactory, httpAuthorizationProvider)
1213
{
13-
this.Service = service;
14+
Service = service;
1415
}
1516
}
1617

@@ -21,13 +22,18 @@ public class HttpTestContext
2122

2223
public HttpTestContext(IHttpClientFactory httpClientFactory, IHttpAuthorizationProvider httpAuthorizationProvider)
2324
{
24-
this.HttpClientFactory = httpClientFactory;
25-
this.HttpAuthorizationProvider = httpAuthorizationProvider;
25+
HttpClientFactory = httpClientFactory;
26+
HttpAuthorizationProvider = httpAuthorizationProvider;
2627
}
2728

28-
public TService CreateService<TService>() => HttpServiceFactory.CreateServiceInstance<TService>(this.HttpClientFactory, this.HttpAuthorizationProvider);
29+
public TService CreateService<TService>() => HttpServiceFactory.CreateServiceInstance<TService>(HttpClientFactory, HttpAuthorizationProvider);
2930

30-
public HttpClient CreateClient() => this.HttpClientFactory.CreateClient(TestHttpClientConfiguration.HttpClientName);
31-
public HttpClient CreateClient(Uri baseAddress) => this.HttpClientFactory.CreateClient(TestHttpClientConfiguration.HttpClientName, baseAddress);
31+
public HttpClient CreateClient() => HttpClientFactory.CreateClient(TestHttpClientFactoryBuilder.HttpClientName);
32+
public HttpClient CreateClient(Uri baseAddress)
33+
{
34+
HttpClient client = CreateClient();
35+
client.BaseAddress = baseAddress;
36+
return client;
37+
}
3238
}
3339
}

src/Dibix.Testing/Http/TestHttpClientConfiguration.cs renamed to src/Dibix.Testing/Http/TestHttpClientFactoryBuilder.cs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,39 @@
1-
using System;
2-
using System.Diagnostics;
1+
using System.Diagnostics;
32
using System.IO;
43
using System.Net.Http;
54
using System.Reflection;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using Dibix.Http.Client;
8+
using Microsoft.Extensions.DependencyInjection;
99
using Microsoft.VisualStudio.TestTools.UnitTesting;
1010

1111
namespace Dibix.Testing.Http
1212
{
13-
internal sealed class TestHttpClientConfiguration : HttpClientConfiguration
13+
internal sealed class TestHttpClientFactoryBuilder : HttpClientFactoryBuilder
1414
{
1515
private readonly TestContext _testContext;
1616
private readonly TextWriter _logger;
17-
private readonly Action<IHttpClientBuilder> _additionalClientConfiguration;
1817

1918
public const string HttpClientName = "Dibix.Testing.TestHttpClient";
20-
public override string Name => HttpClientName;
19+
protected override string ClientName => HttpClientName;
2120

22-
public TestHttpClientConfiguration(TestContext testContext, TextWriter logger, Action<IHttpClientBuilder> additionalClientConfiguration)
21+
private TestHttpClientFactoryBuilder(TestContext testContext, TextWriter logger)
2322
{
24-
this._testContext = testContext;
25-
this._logger = logger;
26-
this._additionalClientConfiguration = additionalClientConfiguration;
23+
_testContext = testContext;
24+
_logger = logger;
2725
}
2826

29-
public override void Configure(IHttpClientBuilder builder)
27+
public static TestHttpClientFactoryBuilder Create(TestContext testContext, TextWriter logger) => new TestHttpClientFactoryBuilder(testContext, logger);
28+
29+
protected override void Configure(Microsoft.Extensions.DependencyInjection.IHttpClientBuilder builder)
3030
{
31-
this._additionalClientConfiguration(builder);
32-
builder.ConfigureClient(this.ConfigureClient);
33-
builder.AddHttpMessageHandler(() => new LoggingHttpMessageHandler(this._logger));
31+
builder.AddHttpMessageHandler(() => new LoggingHttpMessageHandler(_logger));
3432
}
3533

36-
private void ConfigureClient(HttpClient client)
34+
protected override void Configure(HttpClient client)
3735
{
38-
Assembly testAssembly = TestImplementationResolver.ResolveTestAssembly(this._testContext);
36+
Assembly testAssembly = TestImplementationResolver.ResolveTestAssembly(_testContext);
3937
client.AddUserAgent(y => y.FromAssembly(testAssembly, productName =>
4038
{
4139
string normalizedProductName = productName.Replace(".", null);
@@ -47,22 +45,22 @@ private sealed class LoggingHttpMessageHandler : DelegatingHandler
4745
{
4846
private readonly TextWriter _output;
4947

50-
public LoggingHttpMessageHandler(TextWriter output) => this._output = output;
48+
public LoggingHttpMessageHandler(TextWriter output) => _output = output;
5149

5250
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
5351
{
54-
await this._output.WriteAsync($"{request.Method} {request.RequestUri}").ConfigureAwait(false);
52+
await _output.WriteAsync($"{request.Method} {request.RequestUri}").ConfigureAwait(false);
5553
try
5654
{
5755
Stopwatch sw = Stopwatch.StartNew();
5856
HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
5957
sw.Stop();
60-
await this._output.WriteAsync($" {(int)response.StatusCode} {response.StatusCode} {sw.Elapsed}").ConfigureAwait(false);
58+
await _output.WriteAsync($" {(int)response.StatusCode} {response.StatusCode} {sw.Elapsed}").ConfigureAwait(false);
6159
return response;
6260
}
6361
finally
6462
{
65-
await this._output.WriteLineAsync().ConfigureAwait(false);
63+
await _output.WriteLineAsync().ConfigureAwait(false);
6664
}
6765
}
6866
}

src/Dibix.Testing/Http/WebApiServiceTestBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using Dibix.Http.Client;
55
using Dibix.Testing.Data;
6+
using IHttpClientFactory = System.Net.Http.IHttpClientFactory;
67

78
namespace Dibix.Testing.Http
89
{

src/Dibix.Testing/Http/WebApiTestBase.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@
88
using Newtonsoft.Json;
99
using Newtonsoft.Json.Linq;
1010
using Newtonsoft.Json.Serialization;
11+
using IHttpClientBuilder = Microsoft.Extensions.DependencyInjection.IHttpClientBuilder;
12+
using IHttpClientFactory = System.Net.Http.IHttpClientFactory;
1113

1214
namespace Dibix.Testing.Http
1315
{
1416
public abstract class WebApiTestBase<TConfiguration> : DatabaseTestBase<TConfiguration> where TConfiguration : DatabaseConfigurationBase, new()
1517
{
1618
#region Protected Methods
17-
protected virtual Task ExecuteTest(Func<HttpTestContext, Task> testFlow) => this.ExecuteTest(testFlow, CreateTestContext);
19+
protected virtual Task ExecuteTest(Func<HttpTestContext, Task> testFlow) => ExecuteTest(testFlow, CreateTestContext);
1820

1921
private protected async Task ExecuteTest<TTestContext>(Func<TTestContext, Task> testFlow, Func<IHttpClientFactory, IHttpAuthorizationProvider, TTestContext> contextCreator) where TTestContext : HttpTestContext
2022
{
21-
HttpClientConfiguration clientSetup = new TestHttpClientConfiguration(base.TestContext, base.Out, x => this.ConfigureClient(base.Configuration, x));
22-
IHttpClientFactory httpClientFactory = new DefaultHttpClientFactory(clientSetup);
23+
IHttpClientFactory httpClientFactory = TestHttpClientFactoryBuilder.Create(TestContext, Out)
24+
.Configure(x => ConfigureClient(Configuration, x))
25+
.Build();
2326
ITestAuthorizationContext authorizationContext = new TestAuthorizationContext(httpClientFactory);
24-
IHttpAuthorizationProvider authorizationProvider = await this.Authorize(authorizationContext, base.Configuration).ConfigureAwait(false);
27+
IHttpAuthorizationProvider authorizationProvider = await Authorize(authorizationContext, Configuration).ConfigureAwait(false);
2528
TTestContext testContext = contextCreator(httpClientFactory, authorizationProvider);
2629
await testFlow(testContext).ConfigureAwait(false);
2730
}
@@ -30,7 +33,7 @@ private protected async Task ExecuteTest<TTestContext>(Func<TTestContext, Task>
3033
protected async Task<TContent> InvokeApi<TService, TContent>(TService service, Expression<Func<TService, Task<HttpResponse<TContent>>>> methodSelector, string expectedText)
3134
{
3235
HttpResponse<TContent> response = await InvokeApi(service, methodSelector).ConfigureAwait(false);
33-
this.Assert(response, expectedText);
36+
Assert(response, expectedText);
3437
return response.ResponseContent;
3538
}
3639
protected Task InvokeApi<TService>(HttpTestContext context, Expression<Func<TService, Task<HttpResponseMessage>>> methodSelector) => InvokeApiCore<TService, HttpResponseMessage>(context, methodSelector);
@@ -42,7 +45,7 @@ protected async Task<TContent> InvokeApi<TService, TContent>(HttpTestContext con
4245
protected async Task<TContent> InvokeApi<TService, TContent>(HttpTestContext context, Expression<Func<TService, Task<HttpResponse<TContent>>>> methodSelector, string expectedText)
4346
{
4447
HttpResponse<TContent> response = await InvokeApiCore(context, methodSelector).ConfigureAwait(false);
45-
this.Assert(response, expectedText);
48+
Assert(response, expectedText);
4649
return response.ResponseContent;
4750
}
4851

@@ -82,7 +85,7 @@ private void Assert<TContent>(HttpResponse<TContent> response, string expectedTe
8285

8386
return value.Value.ToString();
8487
});
85-
base.AssertEqual(expectedTextReplaced, actualText, extension: "json");
88+
AssertEqual(expectedTextReplaced, actualText, extension: "json");
8689
}
8790

8891
private static HttpTestContext CreateTestContext(IHttpClientFactory httpClientFactory, IHttpAuthorizationProvider authorizationProvider)
@@ -96,10 +99,10 @@ private sealed class TestAuthorizationContext : ITestAuthorizationContext
9699
{
97100
private readonly IHttpClientFactory _httpClientFactory;
98101

99-
public TestAuthorizationContext(IHttpClientFactory httpClientFactory) => this._httpClientFactory = httpClientFactory;
102+
public TestAuthorizationContext(IHttpClientFactory httpClientFactory) => _httpClientFactory = httpClientFactory;
100103

101-
public TService CreateService<TService>() => HttpServiceFactory.CreateServiceInstance<TService>(this._httpClientFactory);
102-
public TService CreateService<TService>(IHttpAuthorizationProvider authorizationProvider) => HttpServiceFactory.CreateServiceInstance<TService>(this._httpClientFactory, authorizationProvider);
104+
public TService CreateService<TService>() => HttpServiceFactory.CreateServiceInstance<TService>(_httpClientFactory);
105+
public TService CreateService<TService>(IHttpAuthorizationProvider authorizationProvider) => HttpServiceFactory.CreateServiceInstance<TService>(_httpClientFactory, authorizationProvider);
103106
}
104107
#endregion
105108
}

0 commit comments

Comments
 (0)