Skip to content

Commit b227699

Browse files
authored
Move assertion methods from extension methods to instance methods (#323)
* Refactor tests to test methods based on the HttpRequestMessageAsserter, so that will be easier to refactor. * Move WithHttpMethod and WithHttpVersion tests * Deprecate HasRequestHeader and HasResponseHeader and move tests for WithHeader to HttpRequestMessageAsserter; * Update changelog * Rewrite tests for WithContent * Small async improvements * Refactor some more tests to follow same pattern * Move several HttpRequestMessagesCheckExtensions to HttpRequestMessageAsserter and specify them on IHttpRequestMessagesCheck interface * Replace NSubstitute with a small amount of custom mocks * Use WithHeader implementation for WithRequestHeader and WithContentHeader * Change WithJsonContent and WithFormUrlEncodedContent to use WithContent and WithHeader methods instead of their own implementation. * Deprecate WithFilter on IHttpRequestMessagesCheck
1 parent a748c5b commit b227699

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1532
-1685
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [0.12] - unplanned
8+
### Deprecated
9+
- The methods `WithRequestHeader` and `WithContentHeader` are deprecated, please use `WithHeader` instead.
10+
- The `WithFilter` method is deprecated and will be made internal or be removed. If there is a specific assertion that is missing, please open an issue.
11+
812
### Removed
913
- .NET 6.0 target, since it is no longer supported
1014
- .NET Framework 4.6.2, 4.7.0 and 4.7.2, since these can't be tested using xUnit v3
1115
- automatic nuget updates by dependabot, since we want to test against the lowest supported nuget version and most of the time dependabot does not choose the right package.
16+
1217
### Added
1318
- Support for .NET 9.0
1419
- Support for .NET 10.0
20+
1521
### Changed
1622
- The TestableHttpMessageHandler now makes a clone of the original request, so that the original request can be disposed.
1723
This change also makes it possible to assert the content on .NET Framework.
24+
- The methods `WithRequestHeader` and `WithContentHeader` now work the same as `WithHeader`, this might lead to slight changes in the behavior since now the headers from both the request and the content of that request are checked.
25+
- Moved `WithHttpMethod`, `WithRequestUri`, `WithHttpVersion`, `WithHeader` and `WithContent` from `HttpRequestMessagesCheckExtensions` to `HttpRequestMessageAsserter` and specify them on the `IHttpRequestMessagesCheck` interface.
1826

1927
## [0.11] - 2024-06-15
2028
### Removed

src/TestableHttpClient/HttpRequestMessageAsserter.cs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,148 @@ public IHttpRequestMessagesCheck WithFilter(Func<HttpRequestMessage, bool> reque
8181
}
8282
return this;
8383
}
84+
85+
/// <summary>
86+
/// Asserts whether requests were made to a given URI based on a pattern.
87+
/// </summary>
88+
/// <param name="pattern">The uri pattern that is expected.</param>
89+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
90+
public IHttpRequestMessagesCheck WithRequestUri(string pattern) => WithRequestUri(pattern, null);
91+
92+
/// <summary>
93+
/// Asserts whether requests were made to a given URI based on a pattern.
94+
/// </summary>
95+
/// <param name="pattern">The uri pattern that is expected.</param>
96+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
97+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
98+
public IHttpRequestMessagesCheck WithRequestUri(string pattern, int expectedNumberOfRequests) => WithRequestUri(pattern, (int?)expectedNumberOfRequests);
99+
100+
private IHttpRequestMessagesCheck WithRequestUri(string pattern, int? expectedNumberOfRequests)
101+
{
102+
Guard.ThrowIfNullOrEmpty(pattern);
103+
104+
var condition = string.Empty;
105+
if (pattern != "*")
106+
{
107+
condition = $"uri pattern '{pattern}'";
108+
}
109+
110+
UriPattern uriPattern = UriPatternParser.Parse(pattern);
111+
112+
return WithFilter(x => x.RequestUri is not null && uriPattern.Matches(x.RequestUri, Options.UriPatternMatchingOptions), expectedNumberOfRequests, condition);
113+
}
114+
115+
/// <summary>
116+
/// Asserts whether requests were made with a given HTTP Method.
117+
/// </summary>
118+
/// <param name="httpMethod">The <seealso cref="HttpMethod"/> that is expected.</param>
119+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
120+
public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod) => WithHttpMethod(httpMethod, null);
121+
122+
/// <summary>
123+
/// Asserts whether requests were made with a given HTTP Method.
124+
/// </summary>
125+
/// <param name="httpMethod">The <seealso cref="HttpMethod"/> that is expected.</param>
126+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
127+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
128+
public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod, int expectedNumberOfRequests) => WithHttpMethod(httpMethod, (int?)expectedNumberOfRequests);
129+
130+
private IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod, int? expectedNumberOfRequests)
131+
{
132+
Guard.ThrowIfNull(httpMethod);
133+
134+
return WithFilter(x => x.HasHttpMethod(httpMethod), expectedNumberOfRequests, $"HTTP Method '{httpMethod}'");
135+
}
136+
137+
/// <summary>
138+
/// Asserts whether requests were made using a specific HTTP Version.
139+
/// </summary>
140+
/// <param name="check">The implementation that hold all the request messages.</param>
141+
/// <param name="httpVersion">The <seealso cref="System.Net.HttpVersion"/> that is expected.</param>
142+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
143+
public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion) => WithHttpVersion(httpVersion, null);
144+
145+
/// <summary>
146+
/// Asserts whether requests were made using a specific HTTP Version.
147+
/// </summary>
148+
/// <param name="httpVersion">The <seealso cref="System.Net.HttpVersion"/> that is expected.</param>
149+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
150+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
151+
public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion, int expectedNumberOfRequests) => WithHttpVersion(httpVersion, (int?)expectedNumberOfRequests);
152+
153+
private IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion, int? expectedNumberOfRequests)
154+
{
155+
Guard.ThrowIfNull(httpVersion);
156+
157+
return WithFilter(x => x.HasHttpVersion(httpVersion), expectedNumberOfRequests, $"HTTP Version '{httpVersion}'");
158+
}
159+
160+
/// <summary>
161+
/// Asserts whether requests were made with a specific header name. Values are ignored.
162+
/// </summary>
163+
/// <param name="headerName">The name of the header that is expected.</param>
164+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
165+
public IHttpRequestMessagesCheck WithHeader(string headerName) => WithHeader(headerName, (int?)null);
166+
167+
/// <summary>
168+
/// Asserts whether requests were made with a specific header name. Values are ignored.
169+
/// </summary>
170+
/// <param name="headerName">The name of the header that is expected.</param>
171+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
172+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
173+
public IHttpRequestMessagesCheck WithHeader(string headerName, int expectedNumberOfRequests) => WithHeader(headerName, (int?)expectedNumberOfRequests);
174+
175+
private IHttpRequestMessagesCheck WithHeader(string headerName, int? expectedNumberOfRequests)
176+
{
177+
Guard.ThrowIfNullOrEmpty(headerName);
178+
179+
return WithFilter(x => x.HasHeader(headerName), expectedNumberOfRequests, $"header '{headerName}'");
180+
}
181+
182+
/// <summary>
183+
/// Asserts whether requests were made with a specific header name and value.
184+
/// </summary>
185+
/// <param name="headerName">The name of the header that is expected.</param>
186+
/// <param name="headerValue">The value of the expected header, supports wildcards.</param>
187+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
188+
public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue) => WithHeader(headerName, headerValue, null);
189+
190+
/// <summary>
191+
/// Asserts whether requests were made with a specific header name and value.
192+
/// </summary>
193+
/// <param name="headerName">The name of the header that is expected.</param>
194+
/// <param name="headerValue">The value of the expected header, supports wildcards.</param>
195+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
196+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
197+
public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue, int expectedNumberOfRequests) => WithHeader(headerName, headerValue, (int?)expectedNumberOfRequests);
198+
199+
private IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue, int? expectedNumberOfRequests)
200+
{
201+
Guard.ThrowIfNullOrEmpty(headerName);
202+
Guard.ThrowIfNullOrEmpty(headerValue);
203+
204+
return WithFilter(x => x.HasHeader(headerName, headerValue), expectedNumberOfRequests, $"header '{headerName}' and value '{headerValue}'");
205+
}
206+
207+
/// <summary>
208+
/// Asserts whether requests were made with specific content.
209+
/// </summary>
210+
/// <param name="pattern">The expected content, supports wildcards.</param>
211+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
212+
public IHttpRequestMessagesCheck WithContent(string pattern) => WithContent(pattern, null);
213+
214+
/// <summary>
215+
/// Asserts whether requests were made with specific content.
216+
/// </summary>
217+
/// <param name="pattern">The expected content, supports wildcards.</param>
218+
/// <param name="expectedNumberOfRequests">The expected number of requests.</param>
219+
/// <returns>The <seealso cref="IHttpRequestMessagesCheck"/> for further assertions.</returns>
220+
public IHttpRequestMessagesCheck WithContent(string pattern, int expectedNumberOfRequests) => WithContent(pattern, (int?)expectedNumberOfRequests);
221+
222+
private IHttpRequestMessagesCheck WithContent(string pattern, int? expectedNumberOfRequests)
223+
{
224+
Guard.ThrowIfNull(pattern);
225+
226+
return WithFilter(x => x.HasContent(pattern), expectedNumberOfRequests, $"content '{pattern}'");
227+
}
84228
}

src/TestableHttpClient/HttpRequestMessageExtensions.cs

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,6 @@ internal static bool HasHttpVersion(this HttpRequestMessage httpRequestMessage,
1919
return httpRequestMessage.Version == httpVersion;
2020
}
2121

22-
/// <summary>
23-
/// Determines whether a specific HttpVersion is set on a request.
24-
/// </summary>
25-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the correct version on.</param>
26-
/// <param name="httpVersion">The expected version.</param>
27-
/// <returns>true when the HttpVersion matches; otherwise, false.</returns>
28-
internal static bool HasHttpVersion(this HttpRequestMessage httpRequestMessage, string httpVersion)
29-
{
30-
Guard.ThrowIfNull(httpRequestMessage);
31-
Guard.ThrowIfNullOrEmpty(httpVersion);
32-
33-
return httpRequestMessage.HasHttpVersion(new Version(httpVersion));
34-
}
35-
3622
/// <summary>
3723
/// Determines whether a specific HttpMethod is set on a request.
3824
/// </summary>
@@ -47,27 +33,24 @@ internal static bool HasHttpMethod(this HttpRequestMessage httpRequestMessage, H
4733
return httpRequestMessage.Method == httpMethod;
4834
}
4935

50-
/// <summary>
51-
/// Determines whether a specific HttpMethod is set on a request.
52-
/// </summary>
53-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the correct method on.</param>
54-
/// <param name="httpMethod">The expected method.</param>
55-
/// <returns>true when the HttpMethod matches; otherwise, false.</returns>
56-
internal static bool HasHttpMethod(this HttpRequestMessage httpRequestMessage, string httpMethod)
36+
internal static bool HasHeader(this HttpRequestMessage httpRequestMessage, string headerName)
5737
{
5838
Guard.ThrowIfNull(httpRequestMessage);
59-
Guard.ThrowIfNullOrEmpty(httpMethod);
39+
Guard.ThrowIfNullOrEmpty(headerName);
6040

61-
return httpRequestMessage.HasHttpMethod(new HttpMethod(httpMethod));
41+
return httpRequestMessage.Headers.HasHeader(headerName) || (httpRequestMessage.Content is not null && httpRequestMessage.Content.Headers.HasHeader(headerName));
6242
}
6343

64-
/// <summary>
65-
/// Determines whether a specific header is set on a request.
66-
/// </summary>
67-
/// <remarks>This method only checks headers in <see cref="HttpRequestHeaders"/></remarks>
68-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the request header on.</param>
69-
/// <param name="headerName">The name of the header to locate on the request.</param>
70-
/// <returns>true when the request contains a header with the specified name; otherwise, false.</returns>
44+
internal static bool HasHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue)
45+
{
46+
Guard.ThrowIfNull(httpRequestMessage);
47+
Guard.ThrowIfNullOrEmpty(headerName);
48+
Guard.ThrowIfNullOrEmpty(headerValue);
49+
50+
return httpRequestMessage.Headers.HasHeader(headerName, headerValue) || (httpRequestMessage.Content is not null && httpRequestMessage.Content.Headers.HasHeader(headerName, headerValue));
51+
}
52+
53+
[Obsolete("Use HasHeader instead.")]
7154
internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage, string headerName)
7255
{
7356
Guard.ThrowIfNull(httpRequestMessage);
@@ -76,14 +59,7 @@ internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage
7659
return httpRequestMessage.Headers.HasHeader(headerName);
7760
}
7861

79-
/// <summary>
80-
/// Determines whether a specific header with a specific value is set on a request.
81-
/// </summary>
82-
/// <remarks>This method only checks headers in <see cref="HttpRequestHeaders"/></remarks>
83-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the request header on.</param>
84-
/// <param name="headerName">The name of the header to locate on the request.</param>
85-
/// <param name="headerValue">The value the header should have. Wildcard is supported.</param>
86-
/// <returns>true when the request contains a header with the specified name and value; otherwise, false.</returns>
62+
[Obsolete("Use HasHeader instead.")]
8763
internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue)
8864
{
8965
Guard.ThrowIfNull(httpRequestMessage);
@@ -93,13 +69,7 @@ internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage
9369
return httpRequestMessage.Headers.HasHeader(headerName, headerValue);
9470
}
9571

96-
/// <summary>
97-
/// Determines whether a specific header is set on a request.
98-
/// </summary>
99-
/// <remarks>This method only checks headers in <see cref="HttpContentHeaders"/></remarks>
100-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the content header on.</param>
101-
/// <param name="headerName">The name of the header to locate on the request content.</param>
102-
/// <returns>true when the request contains a header with the specified name; otherwise, false.</returns>
72+
[Obsolete("Use HasHeader instead.")]
10373
internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage, string headerName)
10474
{
10575
Guard.ThrowIfNull(httpRequestMessage);
@@ -113,14 +83,7 @@ internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage
11383
return httpRequestMessage.Content.Headers.HasHeader(headerName);
11484
}
11585

116-
/// <summary>
117-
/// Determines whether a specific header with a specific value is set on a request.
118-
/// </summary>
119-
/// <remarks>This method only checks headers in <see cref="HttpContentHeaders"/></remarks>
120-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check the content header on.</param>
121-
/// <param name="headerName">The name of the header to locate on the request content.</param>
122-
/// <param name="headerValue">The value the header should have. Wildcard is supported.</param>
123-
/// <returns>true when the request contains a header with the specified name and value; otherwise, false.</returns>
86+
[Obsolete("Use HasHeader instead.")]
12487
internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue)
12588
{
12689
Guard.ThrowIfNull(httpRequestMessage);
@@ -135,18 +98,6 @@ internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage
13598
return httpRequestMessage.Content.Headers.HasHeader(headerName, headerValue);
13699
}
137100

138-
/// <summary>
139-
/// Determines whether the request has content.
140-
/// </summary>
141-
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check for content.</param>
142-
/// <returns>true when the request has content; otherwise, false.</returns>
143-
internal static bool HasContent(this HttpRequestMessage httpRequestMessage)
144-
{
145-
Guard.ThrowIfNull(httpRequestMessage);
146-
147-
return httpRequestMessage.Content != null;
148-
}
149-
150101
/// <summary>
151102
/// Determines whether the request content matches a string pattern.
152103
/// </summary>
@@ -163,7 +114,10 @@ internal static bool HasContent(this HttpRequestMessage httpRequestMessage, stri
163114
return false;
164115
}
165116

166-
var stringContent = httpRequestMessage.Content.ReadAsStringAsync().Result;
117+
var stringContent = httpRequestMessage.Content.ReadAsStringAsync()
118+
.ConfigureAwait(false)
119+
.GetAwaiter()
120+
.GetResult();
167121

168122
return pattern switch
169123
{

0 commit comments

Comments
 (0)