Skip to content

Commit b5200de

Browse files
committed
New HttpMessageOptions class and AppVeyor changes.
- New HttpMessageOptions class so we can now also test for the HttpMethod (GET/POST, etc). - Updated the AppVeyor settings to be more automated and helpful.
1 parent 6eb2968 commit b5200de

12 files changed

+299
-44
lines changed

Code/HttpClient.Helpers.Tests/FakeMessageHandlerFacts.cs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public async Task GivenAFewHttpResponseMessages_GetAsync_ReturnsAFakeResponse()
8383
};
8484

8585
var messageHandler = new FakeHttpMessageHandler(messageResponses);
86-
86+
8787
HttpResponseMessage message;
8888
string content;
8989
using (var httpClient = HttpClientFactory.GetHttpClient(messageHandler))
@@ -162,7 +162,8 @@ public async Task GivenAnHttpResponseMessageAndNoRequestUri_GetAsync_ReturnsAFak
162162
}
163163

164164
[Fact]
165-
public async Task GivenAnHttpResponseMessageAndNoRequestUriAndTheHttpClientFactory_GetAsync_ReturnsAFakeResponse()
165+
public async Task
166+
GivenAnHttpResponseMessageAndNoRequestUriAndTheHttpClientFactory_GetAsync_ReturnsAFakeResponse()
166167
{
167168
// Arrange.
168169
const string responseData = "I am not some Html.";
@@ -225,7 +226,8 @@ public async Task GivenAFewHttpResponseMessagesWithAWildcard_GetAsync_ReturnsThe
225226
}
226227

227228
[Fact]
228-
public async Task GivenAFewHttpResponseMessagesWithAWildcardAndTheHttpClientFactory_GetAsync_ReturnsTheWildcardResponse()
229+
public async Task
230+
GivenAFewHttpResponseMessagesWithAWildcardAndTheHttpClientFactory_GetAsync_ReturnsTheWildcardResponse()
229231
{
230232
// Arrange.
231233
const string requestUrl1 = "http://www.something.com/some/website";
@@ -297,12 +299,68 @@ public void GivenAnHttpClientExceptionAndTheHttpClientFactory_GetAsync_ReturnsAF
297299
{
298300
// Act.
299301
result = Should.Throw<HttpRequestException>(
300-
async () => await httpClient.GetAsync("http://www.something.com/some/website"));
302+
async () => await httpClient.GetAsync("http://www.something.com/some/website"));
301303
}
302304

303305
// Assert.
304306
result.Message.ShouldBe(errorMessage);
305307
}
308+
309+
[Fact]
310+
public async Task GivenAnHttpVerb_DeleteAsync_ReturnsAFakeResponse()
311+
{
312+
// Arrange.
313+
const string requestUrl = "http://www.something.com/some/website";
314+
const string responseData = "Delete me plz";
315+
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);
316+
var options = new HttpMessageOptions
317+
{
318+
RequestUri = requestUrl,
319+
HttpMethod = HttpMethod.Delete,
320+
HttpResponseMessage = messageResponse
321+
};
322+
var messageHandler = new FakeHttpMessageHandler(options);
323+
324+
HttpResponseMessage message;
325+
string content;
326+
using (var httpClient = HttpClientFactory.GetHttpClient(messageHandler))
327+
{
328+
// Act.
329+
message = await httpClient.DeleteAsync(requestUrl);
330+
content = await message.Content.ReadAsStringAsync();
331+
}
332+
333+
// Assert.
334+
message.StatusCode.ShouldBe(HttpStatusCode.OK);
335+
content.ShouldBe(responseData);
336+
}
337+
338+
[Fact]
339+
public void GivenAnInvalidHttpVerb_GetAsync_ThrowsAnException()
340+
{
341+
// Arrange.
342+
const string requestUrl = "http://www.something.com/some/website";
343+
const string responseData = "Delete me plz";
344+
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);
345+
var options = new HttpMessageOptions
346+
{
347+
RequestUri = requestUrl,
348+
HttpMethod = HttpMethod.Delete,
349+
HttpResponseMessage = messageResponse
350+
};
351+
var messageHandler = new FakeHttpMessageHandler(options);
352+
353+
Exception exception;
354+
using (var httpClient = HttpClientFactory.GetHttpClient(messageHandler))
355+
{
356+
// Act.
357+
exception = Should.Throw<InvalidOperationException>(() => httpClient.GetAsync(requestUrl));
358+
}
359+
360+
// Assert.
361+
exception.Message.ShouldBe(
362+
$"No HttpResponseMessage found for the Request Uri: {requestUrl}. Please provide one in the FakeHttpMessageHandler constructor Or use a '*' for any request uri. Search-Key: '{requestUrl}. Setup: 1 responses: DELETE {requestUrl}");
363+
}
306364
}
307365

308366
public class AddMessageFacts

Code/HttpClient.Helpers.Tests/HttpClient.Helpers.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
<WarningLevel>4</WarningLevel>
3333
</PropertyGroup>
3434
<ItemGroup>
35-
<Reference Include="Shouldly, Version=2.4.0.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
36-
<SpecificVersion>False</SpecificVersion>
37-
<HintPath>..\..\packages\Shouldly.2.4.0\lib\net40\Shouldly.dll</HintPath>
35+
<Reference Include="Shouldly, Version=2.8.1.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
36+
<HintPath>..\..\packages\Shouldly.2.8.1\lib\net40\Shouldly.dll</HintPath>
37+
<Private>True</Private>
3838
</Reference>
3939
<Reference Include="System" />
4040
<Reference Include="System.Core" />

Code/HttpClient.Helpers.Tests/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
44
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
55
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
6-
<package id="Shouldly" version="2.4.0" targetFramework="net45" />
6+
<package id="Shouldly" version="2.8.1" targetFramework="net45" />
77
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
88
<package id="xunit.core" version="2.0.0" targetFramework="net45" />
99
<package id="xunit.extensibility.core" version="2.0.0" targetFramework="net45" />

Code/HttpClient.Helpers/FakeMessageHandler.cs

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace WorldDomination.Net.Http
1212
public class FakeHttpMessageHandler : HttpClientHandler
1313
{
1414
private readonly HttpRequestException _exception;
15-
private readonly IDictionary<string, HttpResponseMessage> _responses;
15+
private readonly IDictionary<string, HttpMessageOptions> _lotsOfOptions = new Dictionary<string, HttpMessageOptions>();
1616

1717
/// <summary>
1818
/// A fake message handler.
@@ -21,9 +21,10 @@ public class FakeHttpMessageHandler : HttpClientHandler
2121
/// <param name="requestUri">The endpoint the HttpClient would normally try and connect to.</param>
2222
/// <param name="httpResponseMessage">The faked response message.</param>
2323
public FakeHttpMessageHandler(string requestUri, HttpResponseMessage httpResponseMessage)
24-
: this(new Dictionary<string, HttpResponseMessage>
24+
: this(new HttpMessageOptions
2525
{
26-
{requestUri, httpResponseMessage}
26+
RequestUri = requestUri,
27+
HttpResponseMessage = httpResponseMessage
2728
})
2829
{
2930
}
@@ -32,20 +33,50 @@ public FakeHttpMessageHandler(string requestUri, HttpResponseMessage httpRespons
3233
/// A fake message handler.
3334
/// </summary>
3435
/// <remarks>TIP: If you have a requestUri = "*", this is a catch-all ... so if none of the other requestUri's match, then it will fall back to this dictionary item.</remarks>
35-
/// <param name="httpResponseMessages">A dictionary of request endpoints and their respective fake response message.</param>
36-
public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> httpResponseMessages)
36+
///// <param name="httpResponseMessages">A dictionary of request endpoints and their respective fake response message.</param>
37+
//public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> httpResponseMessages)
38+
//{
39+
// if (httpResponseMessages == null)
40+
// {
41+
// throw new ArgumentNullException(nameof(httpResponseMessages));
42+
// }
43+
44+
// if (!httpResponseMessages.Any())
45+
// {
46+
// throw new ArgumentOutOfRangeException(nameof(httpResponseMessages));
47+
// }
48+
49+
// _responses = httpResponseMessages;
50+
//}
51+
52+
public FakeHttpMessageHandler(HttpMessageOptions options) : this(new List<HttpMessageOptions> { options})
3753
{
38-
if (httpResponseMessages == null)
54+
}
55+
56+
public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> responses)
57+
{
58+
if (responses == null)
3959
{
40-
throw new ArgumentNullException("httpResponseMessages");
60+
throw new ArgumentNullException(nameof(responses));
4161
}
4262

43-
if (!httpResponseMessages.Any())
63+
if (!responses.Any())
4464
{
45-
throw new ArgumentOutOfRangeException("httpResponseMessages");
65+
throw new ArgumentOutOfRangeException(nameof(responses));
4666
}
4767

48-
_responses = httpResponseMessages;
68+
var options = responses.Select(item => new HttpMessageOptions
69+
{
70+
RequestUri = item.Key,
71+
HttpResponseMessage = item.Value
72+
});
73+
74+
Initialize(options);
75+
}
76+
77+
public FakeHttpMessageHandler(IEnumerable<HttpMessageOptions> lotsOfOptions)
78+
{
79+
Initialize(lotsOfOptions);
4980
}
5081

5182
/// <summary>
@@ -54,9 +85,9 @@ public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> httpRespo
5485
/// <remarks>This constructor doesn't care what the request endpoint it. So if you're code is trying to hit multuple endpoints, then it will always return the same response message.</remarks>
5586
/// <param name="httpResponseMessage">The faked response message.</param>
5687
public FakeHttpMessageHandler(HttpResponseMessage httpResponseMessage)
57-
: this(new Dictionary<string, HttpResponseMessage>
88+
: this(new HttpMessageOptions
5889
{
59-
{"*", httpResponseMessage}
90+
HttpResponseMessage = httpResponseMessage
6091
})
6192
{
6293
}
@@ -92,47 +123,51 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
92123

93124
var tcs = new TaskCompletionSource<HttpResponseMessage>();
94125

95-
HttpResponseMessage response;
126+
HttpMessageOptions options;
96127
var requestUri = request.RequestUri.ToString();
97128

98-
// Determine the Response message based upon the Request uri.
129+
// If we don't care
130+
var uniqueKey = CreateDictionaryKey(requestUri, request.Method);
131+
var wildcardKey = CreateDictionaryKey("*", HttpMethod.Get);
132+
133+
// Determine the Response message based upon the Request uri && HttpMethod.
99134
// 1. Exact match.
100135
// 2. Wildcard '*' == I don't care what the Uri is, just use this Response.
101136
// 3. Starts with == this is so we don't have to have a huge string in our test case. Just keeping code a bit cleaner.
102137

103138
// 1) & 3) checks.
104-
if (!_responses.TryGetValue(requestUri, out response) &&
105-
!_responses.TryGetValue("*", out response))
139+
if (!_lotsOfOptions.TryGetValue(uniqueKey, out options) &&
140+
!_lotsOfOptions.TryGetValue(wildcardKey, out options))
106141
{
107142
// 2) Starts-with check.
108-
foreach (var key in _responses.Keys.Where(requestUri.StartsWith))
143+
foreach (var key in _lotsOfOptions.Keys.Where(uniqueKey.StartsWith))
109144
{
110-
response = _responses[key];
145+
options = _lotsOfOptions[key];
111146
break;
112147
}
113148

114-
if (response == null)
149+
if (options == null)
115150
{
116151
// Nope - no keys found exactly OR starting-with...
117152

118-
var responsesText = _responses == null
153+
var responsesText = !_lotsOfOptions.Any()
119154
? "-none-"
120-
: string.Join(";", _responses.Keys);
155+
: string.Join(";", _lotsOfOptions.Values);
121156

122157
var errorMessage =
123158
string.Format(
124159
"No HttpResponseMessage found for the Request Uri: {0}. Please provide one in the FakeHttpMessageHandler constructor Or use a '*' for any request uri. Search-Key: '{1}. Setup: {2} responses: {3}",
125160
request.RequestUri,
126161
requestUri,
127-
_responses == null
162+
!_lotsOfOptions.Any()
128163
? "- no responses -"
129-
: _responses.Count.ToString(),
164+
: _lotsOfOptions.Count.ToString(),
130165
responsesText);
131166
throw new InvalidOperationException(errorMessage);
132167
}
133168
}
134169

135-
tcs.SetResult(response);
170+
tcs.SetResult(options.HttpResponseMessage);
136171
return tcs.Task;
137172
}
138173

@@ -149,5 +184,30 @@ public static HttpResponseMessage GetStringHttpResponseMessage(string content,
149184
Content = new StringContent(content, Encoding.UTF8, mediaType)
150185
};
151186
}
187+
188+
private void Initialize(IEnumerable<HttpMessageOptions> lotsOfOptions)
189+
{
190+
if (lotsOfOptions == null)
191+
{
192+
throw new ArgumentNullException(nameof(lotsOfOptions));
193+
}
194+
195+
if (!lotsOfOptions.Any())
196+
{
197+
throw new ArgumentOutOfRangeException(nameof(lotsOfOptions));
198+
}
199+
200+
foreach (var option in lotsOfOptions)
201+
{
202+
var key = CreateDictionaryKey(option.RequestUri, option.HttpMethod);
203+
_lotsOfOptions[key] = option;
204+
}
205+
}
206+
207+
private static string CreateDictionaryKey(string requestUri, HttpMethod httpMethod)
208+
{
209+
var httpMethodText = httpMethod?.ToString() ?? "*";
210+
return string.Format($"{requestUri}||{httpMethodText}");
211+
}
152212
}
153213
}

Code/HttpClient.Helpers/HttpClient.Helpers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<ItemGroup>
4949
<Compile Include="FakeMessageHandler.cs" />
5050
<Compile Include="HttpClientFactory.cs" />
51+
<Compile Include="HttpMessageOptions.cs" />
5152
<Compile Include="MessageHandlerItem.cs" />
5253
<Compile Include="Properties\AssemblyInfo.cs" />
5354
</ItemGroup>

Code/HttpClient.Helpers/HttpClientFactory.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,17 @@ public static void AddMessageHandler(HttpMessageHandler messageHandler,
1515
{
1616
if (messageHandler == null)
1717
{
18-
throw new ArgumentNullException("messageHandler");
18+
throw new ArgumentNullException(nameof(messageHandler));
1919
}
2020

2121
if (string.IsNullOrWhiteSpace(key))
2222
{
23-
throw new ArgumentNullException("key");
23+
throw new ArgumentNullException(nameof(key));
2424
}
2525

2626
if (MessageHandlers.Value.ContainsKey(key))
2727
{
28-
var errorMessage =
29-
string.Format(
30-
"Unable to add the MessageHandler instance because the key '{0}' -already- exists. Please use another key.",
31-
key);
28+
var errorMessage = string.Format($"Unable to add the MessageHandler instance because the key '{key}' -already- exists. Please use another key.");
3229
throw new Exception(errorMessage);
3330
}
3431

@@ -39,7 +36,7 @@ public static void RemoveMessageHandler(string key)
3936
{
4037
if (string.IsNullOrWhiteSpace(key))
4138
{
42-
throw new ArgumentNullException("key");
39+
throw new ArgumentNullException(nameof(key));
4340
}
4441

4542
MessageHandlers.Value.Remove(key);
@@ -68,7 +65,7 @@ public static HttpClient GetHttpClient(string messageHandlerKey)
6865
{
6966
if (string.IsNullOrWhiteSpace(messageHandlerKey))
7067
{
71-
throw new ArgumentNullException("messageHandlerKey");
68+
throw new ArgumentNullException(nameof(messageHandlerKey));
7269
}
7370

7471
// Determine which message handler we should use.

0 commit comments

Comments
 (0)