Skip to content

Releases: sandermvanvliet/TestableHttpClient

2.10.0.0

30 Apr 09:49
Compare
Choose a tag to compare

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

15 Apr 14:38
Compare
Choose a tag to compare

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

02 Apr 14:40
Compare
Choose a tag to compare

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

26 Mar 14:33
Compare
Choose a tag to compare

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

14 Sep 07:13
0268033
Compare
Choose a tag to compare

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

2.5.0.0

16 Aug 17:58
Compare
Choose a tag to compare

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

22 Jun 13:04
98443d7
Compare
Choose a tag to compare

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

24 Jan 15:24
Compare
Choose a tag to compare

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

17 Jun 12:43
fd45f15
Compare
Choose a tag to compare

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

v1.1.0 - Cookie support

03 Jun 09:04
fd643c6
Compare
Choose a tag to compare

This release adds cookie support on responses.

If a response should include cookies you can configure the request with the AndCookie method:

handler
    .RespondTo(HttpMethod.Get, "/api/info/latest")
    .With(HttpStatus.OK)
    .AndContent("application/json", serializedJson)
    .AndCookie("cookie-name", "cookie-value");

This will add the Set-Cookie HTTP header to the response. The AndCookie method supports the following parameters:

  • expiresAt, DateTme will only be added to the cookie if this value is not null
  • domain, string will only be added to the cookie if this value is not null
  • path, string will only be added to the cookie if this value is not null
  • secure, bool will only be added to the cookie if this value is true
  • sameSite (Lax, Strict, None), if left null the SameSite parameter will not be set on the cookie
  • maxAge, int will only be added to the cookie if this value is not null