Skip to content

Releases: sandermvanvliet/TestableHttpClient

2.11.0.0

Choose a tag to compare

@github-actions github-actions released this 15 Jan 15:13

Add support to configure an expected Authorization header on requests. Contributed by Joshua5 in #26

Usage:

handler
.RespondTo()
.Post()
.ForUrl("/search")
.AndAuthorization(new AuthenticationHeaderValue("Scheme", "Value"))
.With(HttpStatusCode.Forbidden);

2.10.0.0

Choose a tag to compare

@github-actions github-actions released this 30 Apr 09:49

Fixed an issue where the request headers for the content part of the request wouldn't be captured.

For example, sending a request like this:

var stringContent = new StringContent("Hello");
stringContent.Headers.Expires = DateTimeOffset.UtcNow;

var request = new HttpRequestMessage(HttpMethod.Get, "/api/info")
{
Content = stringContent
}

would mean that on the captured request the Expires header wasn't present.

Now, all headers from the request.Content.Headers will be captured.

The headers from the request itself were already captured.

2.9.0.0

Choose a tag to compare

@github-actions github-actions released this 15 Apr 14:38

Add support to configure expected request content.
This can be particularly useful if you want to match a route for a specific request body.

For example:

handler
.RespondTo()
.Post()
.ForUrl("/search")
.ForContent(@"{""params"":{""foo"":""bar""}}")
.With(HttpStatusCode.OK);

will only match when the request body is exactly {"params":{"foo":"bar"}}.

Currently only string content is supported.

2.8.0.0

Choose a tag to compare

@github-actions github-actions released this 02 Apr 14:40

Add support to supply a lambda to create the response that's sent to the client. It allows you to generate a more dynamic response if the path you're configuring requires that.

For example:

handler
.RespondTo(HttpMethod.Get, "/api/entity/blah")
.With(HttpStatusCode.OK)
.AndContent(
"application/json",
req => $@"{{""path"":""{req.RequestUri!.PathAndQuery}""}}");

When calling that endpoint the response will contain the path taken from the request.

2.7.0.0

Choose a tag to compare

@github-actions github-actions released this 26 Mar 14:33

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

For example:

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

2.6.0.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 07:13
0268033

Downgrade Microsoft.Extensions.Http from 7.0.0 to 6.0.0 for greater compatibility.

2.5.0.0

Choose a tag to compare

@github-actions github-actions released this 16 Aug 17:58

This release adds the TestableHttpClientFactory to help with scenarios where you are using a IHttpClientFactory in your code:

The code under test:

public class TheRealComponent
{
private readonly IHttpClientFactory _httpClientFactory;

public TheRealComponent(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}

public async Task<string> ExecuteAsync()
{
var httpClient = _httpClientFactory.CreateClient("TheNameOfTheClient");
return await httpClient.GetStringAsync("https://example.com");
}
}

The test:

var httpClientFactory = new TestableHttpClientFactory();
var handler = httpClientFactory.ConfigureClient("TheNameOfTheClient");

handler
.RespondTo()
.Get()
.ForUrl("https://example.com")
.With(HttpStatus.OK)
.AndContent("text/plain", "Hello, world!");

var sut = new TheRealComponent(httpClientFactory);

var result = await sut.ExecuteAsync();

result
.Should()
.Be("Hello, world!");

The TestableHttpClientFactory will return a new HttpClient instance which is backed by the TestableMessageHandler you've configured in the test.

v2.3.0

Choose a tag to compare

@sandermvanvliet sandermvanvliet released this 22 Jun 13:04
98443d7

This release reworks the way that the configured requests are handled internally. Originally it was a very simple approach that proved to be very difficult to extend with new features over time.
That lead to a lot of kludges in the code to make for example cookie handling work and as a side-effect it made the code hard to understand.

The new approach uses a tree like structure to build the map of configured requests/responses which makes it easier to plug in new behaviour.

Additionally you can now get an overview of the configured requests for use when troubleshooting by calling GetCurrentConfiguration() on a TestableMessageHandler instance:

handler
	.RespondTo()
	.Get()
	.ForUrl("/foo/bar")
	.Accepting("text/xml")
	.With(HttpStatusCode.OK)
	.AndContent("text/xml", "<foo>blah</foo>");

var output = handler.GetCurrentConfiguration();

Debug.WriteLine(output);

will show you:

GET
    *://
        *
            /foo/bar
                Accept: text/xml
                Response:
                    HTTP 200 OK with text/xml payload

The * denotes a wildcard. Here we're using a relative URI which means that it will be matched on any scheme (http://, https://, gopher://) and any authority (host).

When specifying an absolute URI the output includes more details:

handler
	.RespondTo()
	.Get()
	.ForUrl("/foo/bar")
	.Accepting("text/xml")
	.With(HttpStatusCode.OK)
	.AndContent("text/xml", "<foo>blah</foo>");

handler
	.RespondTo()
	.Post()
	.ForUrl("https://tempuri.org:5200/foo/bar")
	.Accepting("text/xml")
	.With(HttpStatusCode.Created)
	.AndContent("text/xml", "<foo>blah</foo>");

var output = handler.GetCurrentConfiguration();

Debug.WriteLine(output);
GET
    *://
        *
            /foo/bar
                Accept: text/xml
                Response:
                    HTTP 200 OK with text/xml payload
POST
    https://
        tempuri.org:5200
            /foo/bar
                Accept: text/xml
                Response:
                    HTTP 201 Created with text/xml payload

Here you can see that the scheme and authority are included. Matching will be exactly on those parameters.

Other changes

Query string parameters

The fluent approach of configuring the request had a slightly odd reading when using the ForQueryStringParameter method:

handler
	.RespondTo()
	.Get()
	.ForUrl("/foo/bar/baz?param=value")
	.ForQueryStringParameter("param").WithAnyValue();

as you can see the For...() is used a bit too much and that makes the code look a bit odd. So a rename has been done to WithQueryStringParameter:

handler
	.RespondTo()
	.Get()
	.ForUrl("/foo/bar/baz?param=value&otherparam=value")
	.WithQueryStringParameter("param").HavingAnyValue()
	.WithQueryStringParameter("otherparam").HavingValue("special");

Likewise WithAnyValue and WithValue have been renamed to HavingAnyValue and HavingValue.

The methods have been marked with the [Obsolete] attribute and will be removed in the upcoming 3.0.0 version.

v2.1.0 - Fluent configuration

Choose a tag to compare

@sandermvanvliet sandermvanvliet released this 24 Jan 15:24

This release introduces a more fluent way to configure the responses. Instead of calling the handler like so:

handler
 .RespondTo(HttpMethod.Post, "/api/some/endpoint")
 .With(HttpStatusCode.Created);

you can now write this as:

handler
 .RespondTo()
 .Post()
 .ForUrl("/api/some/endpoint")
 .With(HttpStatusCode.Created);

The value of the Content-Type header can be set as well using AndContentType:

handler
 .RespondTo()
 .Post()
 .ForUrl("/api/some/endpoint")
 .AndContentType("application/json")
 .With(HttpStatusCode.Created);

The RespondTo(HttpMethod method, string url, string contentType) method is marked as obsolete but as a warning. It will be removed in version 3.x in the future.

v1.2.0 - Fix test hangs

Choose a tag to compare

@sandermvanvliet sandermvanvliet released this 17 Jun 12:43
fd45f15

This release fixes a potential issue where tests would hang when a delay is configured for a request.