Skip to content

Commit e7bc345

Browse files
authored
Added integrationtests to showcase the library
Closes #6
1 parent b82f916 commit e7bc345

11 files changed

+396
-35
lines changed

HttpClientTestHelpers.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1818
README.md = README.md
1919
EndProjectSection
2020
EndProject
21+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientTestHelpers.IntegrationTests", "test\HttpClientTestHelpers.IntegrationTests\HttpClientTestHelpers.IntegrationTests.csproj", "{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}"
22+
EndProject
2123
Global
2224
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2325
Debug|Any CPU = Debug|Any CPU
@@ -52,13 +54,26 @@ Global
5254
{70673E72-C346-4AC2-946D-D9F99816FC72}.Release|x64.Build.0 = Release|Any CPU
5355
{70673E72-C346-4AC2-946D-D9F99816FC72}.Release|x86.ActiveCfg = Release|Any CPU
5456
{70673E72-C346-4AC2-946D-D9F99816FC72}.Release|x86.Build.0 = Release|Any CPU
57+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
59+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|x64.ActiveCfg = Debug|Any CPU
60+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|x64.Build.0 = Debug|Any CPU
61+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|x86.ActiveCfg = Debug|Any CPU
62+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Debug|x86.Build.0 = Debug|Any CPU
63+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
64+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|Any CPU.Build.0 = Release|Any CPU
65+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|x64.ActiveCfg = Release|Any CPU
66+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|x64.Build.0 = Release|Any CPU
67+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|x86.ActiveCfg = Release|Any CPU
68+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE}.Release|x86.Build.0 = Release|Any CPU
5569
EndGlobalSection
5670
GlobalSection(SolutionProperties) = preSolution
5771
HideSolutionNode = FALSE
5872
EndGlobalSection
5973
GlobalSection(NestedProjects) = preSolution
6074
{FD5111E1-2970-4DC4-84DD-E4966E17DCB3} = {4C8914F8-D732-462B-978E-3BB5DBE547D7}
6175
{70673E72-C346-4AC2-946D-D9F99816FC72} = {BBCED492-E92B-4FA8-A4A5-B5A76091F25E}
76+
{37A6C1C0-1117-43DE-BD15-290BC8AD32BE} = {BBCED492-E92B-4FA8-A4A5-B5A76091F25E}
6277
EndGlobalSection
6378
GlobalSection(ExtensibilityGlobals) = postSolution
6479
SolutionGuid = {CD31CAB7-6661-4E80-9A70-BC8BA6B9B764}

src/HttpClientTestHelpers/HttpRequestMessageAsserter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public HttpRequestMessageAsserter WithHeader(string headerName, string headerVal
261261
/// <returns>The <seealso cref="HttpRequestMessageAsserter"/> for further assertions.</returns>
262262
public HttpRequestMessageAsserter WithContent(string pattern)
263263
{
264-
if(pattern == null)
264+
if (pattern == null)
265265
{
266266
throw new ArgumentNullException(nameof(pattern));
267267
}

src/HttpClientTestHelpers/TestableHttpMessageHandler.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ namespace HttpClientTestHelpers
1414
public class TestableHttpMessageHandler : HttpMessageHandler
1515
{
1616
private readonly ConcurrentQueue<HttpRequestMessage> httpRequestMessages = new ConcurrentQueue<HttpRequestMessage>();
17-
private HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
17+
private HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
18+
{
19+
Content = new StringContent(string.Empty)
20+
};
1821

1922
/// <summary>
2023
/// Gets the collection of captured requests made using this HttpMessageHandler.
@@ -52,9 +55,9 @@ public void SimulateTimeout()
5255
/// <summary>
5356
/// Validates that requests have been made, throws an exception when no requests were made.
5457
/// </summary>
55-
public void ShouldHaveMadeRequests()
58+
public HttpRequestMessageAsserter ShouldHaveMadeRequests()
5659
{
57-
_ = new HttpRequestMessageAsserter(Requests).WithUriPattern("*");
60+
return new HttpRequestMessageAsserter(Requests).WithUriPattern("*");
5861
}
5962

6063
/// <summary>
@@ -85,14 +88,14 @@ public void ShouldNotHaveMadeRequests()
8588
/// </summary>
8689
/// <param name="pattern">The uri pattern to validate against, the pattern supports *.</param>
8790
/// <returns>An <seealso cref="HttpRequestMessageAsserter"/> which can be used for further validations.</returns>
88-
public HttpRequestMessageAsserter ShouldNotHaveMadeRequestsTo(string pattern)
91+
public void ShouldNotHaveMadeRequestsTo(string pattern)
8992
{
9093
if (pattern == null)
9194
{
9295
throw new ArgumentNullException(nameof(pattern));
9396
}
9497

95-
return new HttpRequestMessageAsserter(Requests, true).WithUriPattern(pattern);
98+
_ = new HttpRequestMessageAsserter(Requests, true).WithUriPattern(pattern);
9699
}
97100

98101
private class TimeoutHttpResponseMessage : HttpResponseMessage
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[*.cs]
2+
# CA1707: Identifiers should not contain underscores
3+
dotnet_diagnostic.CA1707.severity = none
4+
5+
# CA2007: Consider calling ConfigureAwait on the awaited task
6+
dotnet_diagnostic.CA2007.severity = silent
7+
8+
# CA2234: Pass system uri objects instead of strings
9+
dotnet_diagnostic.CA2234.severity = none
10+
11+
# CA2000: Dispose objects before losing scope
12+
dotnet_diagnostic.CA2000.severity = none
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
using System.Net.Http;
2+
using System.Text;
3+
using System.Threading.Tasks;
4+
5+
using Xunit;
6+
7+
namespace HttpClientTestHelpers.IntegrationTests
8+
{
9+
public class AssertingRequests
10+
{
11+
[Fact]
12+
public void BasicAssertionsWhenNoCallsWereMade()
13+
{
14+
var testHandler = new TestableHttpMessageHandler();
15+
var client = new HttpClient(testHandler);
16+
17+
testHandler.ShouldNotHaveMadeRequests();
18+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests());
19+
}
20+
21+
[Fact]
22+
public async Task WhenAssertingCallsAreNotMade_AndCallsWereMade_AssertionExceptionIsThrow()
23+
{
24+
var testHandler = new TestableHttpMessageHandler();
25+
var client = new HttpClient(testHandler);
26+
27+
_ = await client.GetAsync("https://httpbin.org/get");
28+
29+
testHandler.ShouldHaveMadeRequests();
30+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldNotHaveMadeRequests());
31+
}
32+
33+
34+
[Fact]
35+
public async Task AssertingCallsAreNotMadeToSpecificUri()
36+
{
37+
var testHandler = new TestableHttpMessageHandler();
38+
var client = new HttpClient(testHandler);
39+
40+
_ = await client.GetAsync("https://httpbin.org/get");
41+
42+
testHandler.ShouldNotHaveMadeRequestsTo("https://example.org");
43+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldNotHaveMadeRequestsTo("https://httpbin.org/get"));
44+
}
45+
46+
[Fact]
47+
public async Task AssertingCallsAreMadeToSpecificUriPattern()
48+
{
49+
var testHandler = new TestableHttpMessageHandler();
50+
var client = new HttpClient(testHandler);
51+
52+
_ = await client.GetAsync("https://httpbin.org/get");
53+
54+
testHandler.ShouldHaveMadeRequestsTo("https://*");
55+
testHandler.ShouldHaveMadeRequestsTo("https://*.org/get");
56+
testHandler.ShouldHaveMadeRequestsTo("https://httpbin.org/*");
57+
testHandler.ShouldHaveMadeRequestsTo("*://httpbin.org/get");
58+
testHandler.ShouldHaveMadeRequestsTo("https://httpbin.org/get");
59+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("http://httpbin.org/get"));
60+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("https://httpbin.org/"));
61+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("https://*/post"));
62+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("https://example.org/"));
63+
}
64+
65+
[Fact]
66+
public async Task AssertingCallsUsingUriPattern()
67+
{
68+
var testHandler = new TestableHttpMessageHandler();
69+
var client = new HttpClient(testHandler);
70+
71+
_ = await client.GetAsync("https://httpbin.org/get");
72+
73+
testHandler.ShouldHaveMadeRequests().WithUriPattern("https://*");
74+
testHandler.ShouldHaveMadeRequests().WithUriPattern("https://*.org/get");
75+
testHandler.ShouldHaveMadeRequests().WithUriPattern("https://httpbin.org/*");
76+
testHandler.ShouldHaveMadeRequests().WithUriPattern("*://httpbin.org/get");
77+
testHandler.ShouldHaveMadeRequests().WithUriPattern("https://httpbin.org/get");
78+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithUriPattern("http://httpbin.org/get"));
79+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithUriPattern("https://httpbin.org/"));
80+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithUriPattern("https://*/post"));
81+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithUriPattern("https://example.org/"));
82+
}
83+
84+
[Fact]
85+
public async Task ChainUriPatternAssertions()
86+
{
87+
var testHandler = new TestableHttpMessageHandler();
88+
var client = new HttpClient(testHandler);
89+
90+
_ = await client.GetAsync("https://httpbin.org/get");
91+
92+
testHandler.ShouldHaveMadeRequestsTo("https://*")
93+
.WithUriPattern("*://httpbin.org/*")
94+
.WithUriPattern("*/get");
95+
}
96+
97+
[Fact]
98+
public async Task AssertingHttpMethods()
99+
{
100+
var testHandler = new TestableHttpMessageHandler();
101+
var client = new HttpClient(testHandler);
102+
103+
_ = await client.GetAsync("https://httpbin.org/get");
104+
_ = await client.PostAsync("https://httpbin.org/post", new StringContent(""));
105+
106+
testHandler.ShouldHaveMadeRequestsTo("*/get").WithHttpMethod(HttpMethod.Get);
107+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("*/get").WithHttpMethod(HttpMethod.Post));
108+
testHandler.ShouldHaveMadeRequestsTo("*/post").WithHttpMethod(HttpMethod.Post);
109+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequestsTo("*/post").WithHttpMethod(HttpMethod.Get));
110+
}
111+
112+
[Fact]
113+
public async Task AssertingRequestHeaders()
114+
{
115+
var testHandler = new TestableHttpMessageHandler();
116+
var client = new HttpClient(testHandler);
117+
client.DefaultRequestHeaders.Add("api-version", "1.0");
118+
_ = await client.GetAsync("https://httpbin.org/get");
119+
120+
testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version");
121+
testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1.0");
122+
testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1*");
123+
124+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("my-version"));
125+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1"));
126+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "2*"));
127+
}
128+
129+
[Fact]
130+
public async Task AssertingContentHeaders()
131+
{
132+
var testHandler = new TestableHttpMessageHandler();
133+
var client = new HttpClient(testHandler);
134+
135+
_ = await client.PostAsync("https://httpbin.org/post", new StringContent("", Encoding.UTF8, "application/json"));
136+
137+
testHandler.ShouldHaveMadeRequests().WithContentHeader("content-type");
138+
testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type");
139+
testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json; charset=utf-8");
140+
testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json*");
141+
testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "*charset=utf-8");
142+
143+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Disposition"));
144+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json"));
145+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "*=utf-16"));
146+
}
147+
148+
[Fact]
149+
public async Task AssertingContent()
150+
{
151+
var testHandler = new TestableHttpMessageHandler();
152+
var client = new HttpClient(testHandler);
153+
154+
_ = await client.PostAsync("https://httpbin.org/post", new StringContent("my special content"));
155+
156+
testHandler.ShouldHaveMadeRequests().WithContent("my special content");
157+
testHandler.ShouldHaveMadeRequests().WithContent("my*content");
158+
testHandler.ShouldHaveMadeRequests().WithContent("*");
159+
160+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithContent(""));
161+
Assert.Throws<HttpRequestMessageAssertionException>(() => testHandler.ShouldHaveMadeRequests().WithContent("my"));
162+
}
163+
164+
[Fact]
165+
public async Task CustomAssertions()
166+
{
167+
var testHandler = new TestableHttpMessageHandler();
168+
var client = new HttpClient(testHandler);
169+
170+
_ = await client.PostAsync("https://httpbin.org/post", new StringContent("", Encoding.UTF8, "application/json"));
171+
172+
testHandler.ShouldHaveMadeRequests().With(x => x.HasContentHeader("Content-Type", "application/json") || x.HasContentHeader("Content-Type", "application/json; *"), "");
173+
}
174+
}
175+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
6+
using Xunit;
7+
8+
namespace HttpClientTestHelpers.IntegrationTests
9+
{
10+
public class ConfigureResponses
11+
{
12+
[Fact]
13+
public async Task UsingTestHandler_WithoutSettingUpResponse_Returns200OKWithoutContent()
14+
{
15+
using var testHandler = new TestableHttpMessageHandler();
16+
17+
using var httpClient = new HttpClient(testHandler);
18+
var result = await httpClient.GetAsync("http://httpbin.org/status/200");
19+
20+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
21+
Assert.Equal(string.Empty, await result.Content.ReadAsStringAsync());
22+
}
23+
24+
[Fact]
25+
public async Task UsingTestHandlerWithCustomRepsonse_ReturnsCustomResponse()
26+
{
27+
using var testHandler = new TestableHttpMessageHandler();
28+
using var response = new HttpResponseMessage(HttpStatusCode.Created)
29+
{
30+
Content = new StringContent("HttpClient testing is easy", Encoding.UTF8, "text/plain")
31+
};
32+
testHandler.RespondWith(response);
33+
34+
using var httpClient = new HttpClient(testHandler);
35+
var result = await httpClient.GetAsync("http://httpbin.org/status/201");
36+
37+
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
38+
Assert.Equal("HttpClient testing is easy", await result.Content.ReadAsStringAsync());
39+
}
40+
41+
[Fact]
42+
public async Task UsingTestHandlerWithMultipleCustomRepsonse_ReturnsLastCustomResponse()
43+
{
44+
using var testHandler = new TestableHttpMessageHandler();
45+
using var response = new HttpResponseMessage(HttpStatusCode.Created)
46+
{
47+
Content = new StringContent("HttpClient testing is easy", Encoding.UTF8, "text/plain")
48+
};
49+
using var realResponse = new HttpResponseMessage(HttpStatusCode.NotFound)
50+
{
51+
Content = new StringContent("Not Found")
52+
};
53+
testHandler.RespondWith(response);
54+
testHandler.RespondWith(realResponse);
55+
56+
using var httpClient = new HttpClient(testHandler);
57+
var result = await httpClient.GetAsync("http://httpbin.org/status/201");
58+
59+
Assert.Equal(HttpStatusCode.NotFound, result.StatusCode);
60+
Assert.Equal("Not Found", await result.Content.ReadAsStringAsync());
61+
}
62+
63+
[Fact]
64+
public async Task UsingTestHandlerWithCustomResponse_AlwaysReturnsSameCustomResponse()
65+
{
66+
using var testHandler = new TestableHttpMessageHandler();
67+
using var response = new HttpResponseMessage(HttpStatusCode.Created)
68+
{
69+
Content = new StringContent("HttpClient testing is easy", Encoding.UTF8, "text/plain")
70+
};
71+
testHandler.RespondWith(response);
72+
73+
using var httpClient = new HttpClient(testHandler);
74+
var urls = new[]
75+
{
76+
"http://httpbin.org/status/200",
77+
"http://httpbin.org/status/201",
78+
"http://httpbin.org/status/400",
79+
"http://httpbin.org/status/401",
80+
"http://httpbin.org/status/503",
81+
};
82+
83+
foreach (var url in urls)
84+
{
85+
var result = await httpClient.GetAsync(url);
86+
87+
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
88+
Assert.Equal("HttpClient testing is easy", await result.Content.ReadAsStringAsync());
89+
}
90+
}
91+
92+
[Fact]
93+
public async Task SimulateTimeout_WillThrowExceptionSimulatingTheTimeout()
94+
{
95+
using var testHandler = new TestableHttpMessageHandler();
96+
testHandler.SimulateTimeout();
97+
98+
using var httpClient = new HttpClient(testHandler);
99+
await Assert.ThrowsAsync<TaskCanceledException>(() => httpClient.GetAsync("https://httpbin.org/delay/500"));
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)