Releases: sandermvanvliet/TestableHttpClient
2.10.0.0
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
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
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
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
Downgrade Microsoft.Extensions.Http from 7.0.0 to 6.0.0 for greater compatibility.
2.5.0.0
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
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
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
This release fixes a potential issue where tests would hang when a delay is configured for a request.
v1.1.0 - Cookie support
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 notnull
domain
,string
will only be added to the cookie if this value is notnull
path
,string
will only be added to the cookie if this value is notnull
secure
,bool
will only be added to the cookie if this value istrue
sameSite
(Lax
,Strict
,None
), if leftnull
theSameSite
parameter will not be set on the cookiemaxAge
,int
will only be added to the cookie if this value is notnull