Test Polly retry polly configured via Startup.ConfigureServices() with ASP.NET Core API
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I want to find out how Polly retry polly configured via Startup.ConfigureServices() can be tested.
ConfigureServices
Polly policy is configured within it
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<IHttpClientService, HttpClientService>()
.SetWaitAndRetryPolicy1();
}
}
Below is the Polly policy:
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy1(this IHttpClientBuilder clientBuilder)
{
clientBuilder.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
);
}
}
Below is what I tried:
Integration test
The Polly policy is configured within the test.
public class RetryPolicyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public RetryPolicyTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("http://localhost:1234/api/v1/car/")]
public async Task Test3(string url)
{
// Arrange
var client = _factory.WithWebHostBuilder(whb =>
{
whb.ConfigureServices((bc, sc) =>
{
sc.AddOptions();
sc.AddHttpClient("test")
.SetWaitAndRetryPolicy1(); //Test the Polly policy
sc.BuildServiceProvider();
});
})
.CreateClient(); //cannot get a named or typed HttpClient
// Act
var body = "{}";
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
var response = await client.PostAsync(url, content);
}
//Assert: somewhy assert it
}
}
}
The problem is that
I cannot retrieve the HttpClient that has been configured with the Polly polly. Because WebApplicationFactory.CreateClient() has no overloads that returns a named or typed HttpClient:
Any idea?
Is there a better way to testing it?
ASPS.NET Core API 2.2
c# asp.net-core .net-core asp.net-core-webapi polly
add a comment |
I want to find out how Polly retry polly configured via Startup.ConfigureServices() can be tested.
ConfigureServices
Polly policy is configured within it
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<IHttpClientService, HttpClientService>()
.SetWaitAndRetryPolicy1();
}
}
Below is the Polly policy:
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy1(this IHttpClientBuilder clientBuilder)
{
clientBuilder.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
);
}
}
Below is what I tried:
Integration test
The Polly policy is configured within the test.
public class RetryPolicyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public RetryPolicyTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("http://localhost:1234/api/v1/car/")]
public async Task Test3(string url)
{
// Arrange
var client = _factory.WithWebHostBuilder(whb =>
{
whb.ConfigureServices((bc, sc) =>
{
sc.AddOptions();
sc.AddHttpClient("test")
.SetWaitAndRetryPolicy1(); //Test the Polly policy
sc.BuildServiceProvider();
});
})
.CreateClient(); //cannot get a named or typed HttpClient
// Act
var body = "{}";
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
var response = await client.PostAsync(url, content);
}
//Assert: somewhy assert it
}
}
}
The problem is that
I cannot retrieve the HttpClient that has been configured with the Polly polly. Because WebApplicationFactory.CreateClient() has no overloads that returns a named or typed HttpClient:
Any idea?
Is there a better way to testing it?
ASPS.NET Core API 2.2
c# asp.net-core .net-core asp.net-core-webapi polly
add a comment |
I want to find out how Polly retry polly configured via Startup.ConfigureServices() can be tested.
ConfigureServices
Polly policy is configured within it
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<IHttpClientService, HttpClientService>()
.SetWaitAndRetryPolicy1();
}
}
Below is the Polly policy:
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy1(this IHttpClientBuilder clientBuilder)
{
clientBuilder.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
);
}
}
Below is what I tried:
Integration test
The Polly policy is configured within the test.
public class RetryPolicyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public RetryPolicyTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("http://localhost:1234/api/v1/car/")]
public async Task Test3(string url)
{
// Arrange
var client = _factory.WithWebHostBuilder(whb =>
{
whb.ConfigureServices((bc, sc) =>
{
sc.AddOptions();
sc.AddHttpClient("test")
.SetWaitAndRetryPolicy1(); //Test the Polly policy
sc.BuildServiceProvider();
});
})
.CreateClient(); //cannot get a named or typed HttpClient
// Act
var body = "{}";
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
var response = await client.PostAsync(url, content);
}
//Assert: somewhy assert it
}
}
}
The problem is that
I cannot retrieve the HttpClient that has been configured with the Polly polly. Because WebApplicationFactory.CreateClient() has no overloads that returns a named or typed HttpClient:
Any idea?
Is there a better way to testing it?
ASPS.NET Core API 2.2
c# asp.net-core .net-core asp.net-core-webapi polly
I want to find out how Polly retry polly configured via Startup.ConfigureServices() can be tested.
ConfigureServices
Polly policy is configured within it
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<IHttpClientService, HttpClientService>()
.SetWaitAndRetryPolicy1();
}
}
Below is the Polly policy:
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy1(this IHttpClientBuilder clientBuilder)
{
clientBuilder.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
);
}
}
Below is what I tried:
Integration test
The Polly policy is configured within the test.
public class RetryPolicyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public RetryPolicyTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("http://localhost:1234/api/v1/car/")]
public async Task Test3(string url)
{
// Arrange
var client = _factory.WithWebHostBuilder(whb =>
{
whb.ConfigureServices((bc, sc) =>
{
sc.AddOptions();
sc.AddHttpClient("test")
.SetWaitAndRetryPolicy1(); //Test the Polly policy
sc.BuildServiceProvider();
});
})
.CreateClient(); //cannot get a named or typed HttpClient
// Act
var body = "{}";
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
var response = await client.PostAsync(url, content);
}
//Assert: somewhy assert it
}
}
}
The problem is that
I cannot retrieve the HttpClient that has been configured with the Polly polly. Because WebApplicationFactory.CreateClient() has no overloads that returns a named or typed HttpClient:
Any idea?
Is there a better way to testing it?
ASPS.NET Core API 2.2
c# asp.net-core .net-core asp.net-core-webapi polly
c# asp.net-core .net-core asp.net-core-webapi polly
edited Jan 4 at 15:28
Pingpong
asked Jan 3 at 21:37
PingpongPingpong
2,426124499
2,426124499
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
To modify your posted code minimally to obtain the named or typed HttpClient configured on HttpClientFactory, build the IServiceProvider, obtain the IHttpClientFactory and then obtain the configured client from IHttpClientFactory.
var configuredClient = sc.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("test");
Many people consider the use of IServiceProvider like this to be a service-locator anti-pattern in production code; perhaps it is ok here in a test, to pull the specific item you want to unit-test out of the default app configuration. However, there are also shorter ways for a test to get a sample HttpClient configured on HttpClientFactory, without using a full WebApplicationFactory (see last part of answer).
For a full end-to-end integration test, testing how your app uses the configured policy, using WebApplicationFactory to exercise some endpoint of your app like http://localhost:1234/api/v1/car/:
You could - within the integration test - use a tool like Mountebank for .NET or HttpClientInterception to stub out the calls that the configured HttpClient makes, so that those calls return errors which you expect the policy to handle.
You could use the ability of WebHostBuilder.ConfigureServices(...) to modify the normal startup of your app, to make it easy to assert something to prove the policy was called. For example, you could configure a mock/fake ILog implementation, and assert that the ILog.Error(...) call in your onRetry delegate takes place.
For the shortest-possible, self-contained unit test to test a Polly policy configured on a given HttpClient configuration on HttpClientFactory, you could use a code pattern like below. This only uses IHttpClientFactory and the standard Microsoft DI infrastructure; no web host from ASP.NET.
public class HttpClientFactory_Polly_Policy_Test
{
[Fact]
public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
{
// Given / Arrange
IServiceCollection services = new ServiceCollection();
bool retryCalled = false;
HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;
const string TestClient = "TestClient";
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3, onRetry: (_, __) => retryCalled = true))
.AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient(TestClient);
// When / Act
var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");
// Then / Assert
Assert.Equal(codeHandledByPolicy, result.StatusCode);
Assert.True(retryCalled);
}
}
public class StubDelegatingHandler : DelegatingHandler
{
private readonly HttpStatusCode stubHttpStatusCode;
public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}
If the declarations of policies are pulled out to methods (like SetWaitAndRetryPolicy1() in your posted code), an approach like above provides a more unit-test-focused way to test them.
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54030145%2ftest-polly-retry-polly-configured-via-startup-configureservices-with-asp-net-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
To modify your posted code minimally to obtain the named or typed HttpClient configured on HttpClientFactory, build the IServiceProvider, obtain the IHttpClientFactory and then obtain the configured client from IHttpClientFactory.
var configuredClient = sc.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("test");
Many people consider the use of IServiceProvider like this to be a service-locator anti-pattern in production code; perhaps it is ok here in a test, to pull the specific item you want to unit-test out of the default app configuration. However, there are also shorter ways for a test to get a sample HttpClient configured on HttpClientFactory, without using a full WebApplicationFactory (see last part of answer).
For a full end-to-end integration test, testing how your app uses the configured policy, using WebApplicationFactory to exercise some endpoint of your app like http://localhost:1234/api/v1/car/:
You could - within the integration test - use a tool like Mountebank for .NET or HttpClientInterception to stub out the calls that the configured HttpClient makes, so that those calls return errors which you expect the policy to handle.
You could use the ability of WebHostBuilder.ConfigureServices(...) to modify the normal startup of your app, to make it easy to assert something to prove the policy was called. For example, you could configure a mock/fake ILog implementation, and assert that the ILog.Error(...) call in your onRetry delegate takes place.
For the shortest-possible, self-contained unit test to test a Polly policy configured on a given HttpClient configuration on HttpClientFactory, you could use a code pattern like below. This only uses IHttpClientFactory and the standard Microsoft DI infrastructure; no web host from ASP.NET.
public class HttpClientFactory_Polly_Policy_Test
{
[Fact]
public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
{
// Given / Arrange
IServiceCollection services = new ServiceCollection();
bool retryCalled = false;
HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;
const string TestClient = "TestClient";
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3, onRetry: (_, __) => retryCalled = true))
.AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient(TestClient);
// When / Act
var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");
// Then / Assert
Assert.Equal(codeHandledByPolicy, result.StatusCode);
Assert.True(retryCalled);
}
}
public class StubDelegatingHandler : DelegatingHandler
{
private readonly HttpStatusCode stubHttpStatusCode;
public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}
If the declarations of policies are pulled out to methods (like SetWaitAndRetryPolicy1() in your posted code), an approach like above provides a more unit-test-focused way to test them.
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
add a comment |
To modify your posted code minimally to obtain the named or typed HttpClient configured on HttpClientFactory, build the IServiceProvider, obtain the IHttpClientFactory and then obtain the configured client from IHttpClientFactory.
var configuredClient = sc.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("test");
Many people consider the use of IServiceProvider like this to be a service-locator anti-pattern in production code; perhaps it is ok here in a test, to pull the specific item you want to unit-test out of the default app configuration. However, there are also shorter ways for a test to get a sample HttpClient configured on HttpClientFactory, without using a full WebApplicationFactory (see last part of answer).
For a full end-to-end integration test, testing how your app uses the configured policy, using WebApplicationFactory to exercise some endpoint of your app like http://localhost:1234/api/v1/car/:
You could - within the integration test - use a tool like Mountebank for .NET or HttpClientInterception to stub out the calls that the configured HttpClient makes, so that those calls return errors which you expect the policy to handle.
You could use the ability of WebHostBuilder.ConfigureServices(...) to modify the normal startup of your app, to make it easy to assert something to prove the policy was called. For example, you could configure a mock/fake ILog implementation, and assert that the ILog.Error(...) call in your onRetry delegate takes place.
For the shortest-possible, self-contained unit test to test a Polly policy configured on a given HttpClient configuration on HttpClientFactory, you could use a code pattern like below. This only uses IHttpClientFactory and the standard Microsoft DI infrastructure; no web host from ASP.NET.
public class HttpClientFactory_Polly_Policy_Test
{
[Fact]
public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
{
// Given / Arrange
IServiceCollection services = new ServiceCollection();
bool retryCalled = false;
HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;
const string TestClient = "TestClient";
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3, onRetry: (_, __) => retryCalled = true))
.AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient(TestClient);
// When / Act
var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");
// Then / Assert
Assert.Equal(codeHandledByPolicy, result.StatusCode);
Assert.True(retryCalled);
}
}
public class StubDelegatingHandler : DelegatingHandler
{
private readonly HttpStatusCode stubHttpStatusCode;
public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}
If the declarations of policies are pulled out to methods (like SetWaitAndRetryPolicy1() in your posted code), an approach like above provides a more unit-test-focused way to test them.
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
add a comment |
To modify your posted code minimally to obtain the named or typed HttpClient configured on HttpClientFactory, build the IServiceProvider, obtain the IHttpClientFactory and then obtain the configured client from IHttpClientFactory.
var configuredClient = sc.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("test");
Many people consider the use of IServiceProvider like this to be a service-locator anti-pattern in production code; perhaps it is ok here in a test, to pull the specific item you want to unit-test out of the default app configuration. However, there are also shorter ways for a test to get a sample HttpClient configured on HttpClientFactory, without using a full WebApplicationFactory (see last part of answer).
For a full end-to-end integration test, testing how your app uses the configured policy, using WebApplicationFactory to exercise some endpoint of your app like http://localhost:1234/api/v1/car/:
You could - within the integration test - use a tool like Mountebank for .NET or HttpClientInterception to stub out the calls that the configured HttpClient makes, so that those calls return errors which you expect the policy to handle.
You could use the ability of WebHostBuilder.ConfigureServices(...) to modify the normal startup of your app, to make it easy to assert something to prove the policy was called. For example, you could configure a mock/fake ILog implementation, and assert that the ILog.Error(...) call in your onRetry delegate takes place.
For the shortest-possible, self-contained unit test to test a Polly policy configured on a given HttpClient configuration on HttpClientFactory, you could use a code pattern like below. This only uses IHttpClientFactory and the standard Microsoft DI infrastructure; no web host from ASP.NET.
public class HttpClientFactory_Polly_Policy_Test
{
[Fact]
public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
{
// Given / Arrange
IServiceCollection services = new ServiceCollection();
bool retryCalled = false;
HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;
const string TestClient = "TestClient";
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3, onRetry: (_, __) => retryCalled = true))
.AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient(TestClient);
// When / Act
var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");
// Then / Assert
Assert.Equal(codeHandledByPolicy, result.StatusCode);
Assert.True(retryCalled);
}
}
public class StubDelegatingHandler : DelegatingHandler
{
private readonly HttpStatusCode stubHttpStatusCode;
public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}
If the declarations of policies are pulled out to methods (like SetWaitAndRetryPolicy1() in your posted code), an approach like above provides a more unit-test-focused way to test them.
To modify your posted code minimally to obtain the named or typed HttpClient configured on HttpClientFactory, build the IServiceProvider, obtain the IHttpClientFactory and then obtain the configured client from IHttpClientFactory.
var configuredClient = sc.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("test");
Many people consider the use of IServiceProvider like this to be a service-locator anti-pattern in production code; perhaps it is ok here in a test, to pull the specific item you want to unit-test out of the default app configuration. However, there are also shorter ways for a test to get a sample HttpClient configured on HttpClientFactory, without using a full WebApplicationFactory (see last part of answer).
For a full end-to-end integration test, testing how your app uses the configured policy, using WebApplicationFactory to exercise some endpoint of your app like http://localhost:1234/api/v1/car/:
You could - within the integration test - use a tool like Mountebank for .NET or HttpClientInterception to stub out the calls that the configured HttpClient makes, so that those calls return errors which you expect the policy to handle.
You could use the ability of WebHostBuilder.ConfigureServices(...) to modify the normal startup of your app, to make it easy to assert something to prove the policy was called. For example, you could configure a mock/fake ILog implementation, and assert that the ILog.Error(...) call in your onRetry delegate takes place.
For the shortest-possible, self-contained unit test to test a Polly policy configured on a given HttpClient configuration on HttpClientFactory, you could use a code pattern like below. This only uses IHttpClientFactory and the standard Microsoft DI infrastructure; no web host from ASP.NET.
public class HttpClientFactory_Polly_Policy_Test
{
[Fact]
public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
{
// Given / Arrange
IServiceCollection services = new ServiceCollection();
bool retryCalled = false;
HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;
const string TestClient = "TestClient";
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3, onRetry: (_, __) => retryCalled = true))
.AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient(TestClient);
// When / Act
var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");
// Then / Assert
Assert.Equal(codeHandledByPolicy, result.StatusCode);
Assert.True(retryCalled);
}
}
public class StubDelegatingHandler : DelegatingHandler
{
private readonly HttpStatusCode stubHttpStatusCode;
public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}
If the declarations of policies are pulled out to methods (like SetWaitAndRetryPolicy1() in your posted code), an approach like above provides a more unit-test-focused way to test them.
edited Jan 6 at 7:49
answered Jan 4 at 23:39
mountain travellermountain traveller
2,831819
2,831819
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
add a comment |
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
This is a shortened version of a longer post (more information about the end-to-end integration approach particularly), at; github.com/App-vNext/Polly/issues/555
– mountain traveller
Jan 4 at 23:40
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54030145%2ftest-polly-retry-polly-configured-via-startup-configureservices-with-asp-net-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown