Skip to content

Commit 1445772

Browse files
authored
Sample Wiki tests (#15)
1 parent ecde7d3 commit 1445772

File tree

10 files changed

+240
-24
lines changed

10 files changed

+240
-24
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ CLI: `install-package WorldDomination.HttpClient.Helpers`
3030
## Sample Code
3131

3232
There's [plenty more examples](https://github.com/PureKrome/HttpClient.Helpers/wiki) about to wire up:
33-
- [A really simple example](https://github.com/PureKrome/HttpClient.Helpers/wiki/Constructor-Injection)
33+
- [A really simple example](https://github.com/PureKrome/HttpClient.Helpers/wiki/A-single-endpoint)
3434
- [Multiple endpoints](https://github.com/PureKrome/HttpClient.Helpers/wiki/Multiple-endpoints) at once
3535
- [Wildcard endpoints](https://github.com/PureKrome/HttpClient.Helpers/wiki/Wildcard-endpoints)
3636
- [Throwing exceptions](https://github.com/PureKrome/HttpClient.Helpers/wiki/Faking-an-Exception) and handling it

src/HttpClient.Helpers/FakeMessageHandler.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Linq;
44
using System.Net;
55
using System.Net.Http;
6-
using System.Reflection;
76
using System.Text;
87
using System.Threading;
98
using System.Threading.Tasks;
@@ -16,26 +15,10 @@ public class FakeHttpMessageHandler : HttpClientHandler
1615

1716
private readonly IDictionary<string, HttpMessageOptions> _lotsOfOptions = new Dictionary<string, HttpMessageOptions>();
1817

19-
2018
/// <summary>
2119
/// A fake message handler.
2220
/// </summary>
2321
/// <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>
24-
///// <param name="httpResponseMessages">A dictionary of request endpoints and their respective fake response message.</param>
25-
//public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> httpResponseMessages)
26-
//{
27-
// if (httpResponseMessages == null)
28-
// {
29-
// throw new ArgumentNullException(nameof(httpResponseMessages));
30-
// }
31-
32-
// if (!httpResponseMessages.Any())
33-
// {
34-
// throw new ArgumentOutOfRangeException(nameof(httpResponseMessages));
35-
// }
36-
37-
// _responses = httpResponseMessages;
38-
//}
3922
public FakeHttpMessageHandler(HttpMessageOptions options) : this(new List<HttpMessageOptions>
4023
{
4124
options
@@ -63,12 +46,6 @@ public FakeHttpMessageHandler(HttpRequestException exception)
6346
_exception = exception;
6447
}
6548

66-
/// <summary>
67-
///
68-
/// </summary>
69-
/// <param name="request"></param>
70-
/// <param name="cancellationToken"></param>
71-
/// <returns></returns>
7249
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
7350
CancellationToken cancellationToken)
7451
{

src/HttpClient.Helpers/HttpMessageOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ public HttpContent HttpContent
5151
}
5252
}
5353

54+
// Note: I'm using reflection to set the value in here because I want this value to be _read-only_.
55+
// Secondly, this occurs during a UNIT TEST, so I consider the expensive reflection costs to be
56+
// acceptable in this situation.
5457
public int NumberOfTimesCalled { get; private set; }
5558

5659
public override string ToString()

tests/HttpClient.Helpers.Tests/HttpClient.Helpers.Tests.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
<WarningLevel>4</WarningLevel>
3333
</PropertyGroup>
3434
<ItemGroup>
35+
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
36+
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
37+
<Private>True</Private>
38+
</Reference>
3539
<Reference Include="Shouldly, Version=2.8.2.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
3640
<HintPath>..\..\packages\Shouldly.2.8.2\lib\net40\Shouldly.dll</HintPath>
3741
<Private>True</Private>
@@ -63,10 +67,15 @@
6367
</Reference>
6468
</ItemGroup>
6569
<ItemGroup>
70+
<Compile Include="Wiki Examples\Baa.cs" />
71+
<Compile Include="Wiki Examples\Foo.cs" />
72+
<Compile Include="Wiki Examples\MultipleEndpointTests.cs" />
6673
<Compile Include="GetAsyncTests.cs" />
6774
<Compile Include="GetStringAsyncTests.cs" />
6875
<Compile Include="PostAsyncTests.cs" />
6976
<Compile Include="Properties\AssemblyInfo.cs" />
77+
<Compile Include="Wiki Examples\MyService.cs" />
78+
<Compile Include="Wiki Examples\SingleEndpointTests.cs" />
7079
</ItemGroup>
7180
<ItemGroup>
7281
<None Include="packages.config" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
2+
{
3+
public class Baa
4+
{
5+
public string FavGame { get; set; }
6+
public string FavMovie { get; set; }
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
2+
{
3+
public class Foo
4+
{
5+
public int Id { get; set; }
6+
public string Name { get; set; }
7+
public Baa Baa { get; set; }
8+
}
9+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Threading.Tasks;
2+
using Shouldly;
3+
using WorldDomination.Net.Http;
4+
using Xunit;
5+
6+
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
7+
{
8+
public class MultipleEndpointTests
9+
{
10+
[Fact]
11+
public async Task GivenSomeValidHttpRequests_GetSomeDataAsync_ReturnsAFoo()
12+
{
13+
// Arrange.
14+
15+
// 1. First fake response.
16+
const string responseData1 = "{ \"Id\":69, \"Name\":\"Jane\" }";
17+
var messageResponse1 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData1);
18+
19+
// 2. Second fake response.
20+
const string responseData2 = "{ \"FavGame\":\"Star Wars\", \"FavMovie\":\"Star Wars - all of em\" }";
21+
var messageResponse2 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData2);
22+
23+
// Prepare our 'options' with all of the above fake stuff.
24+
var options = new[]
25+
{
26+
new HttpMessageOptions
27+
{
28+
RequestUri = MyService.GetFooEndPoint,
29+
HttpResponseMessage = messageResponse1
30+
},
31+
new HttpMessageOptions
32+
{
33+
RequestUri = MyService.GetBaaEndPoint,
34+
HttpResponseMessage = messageResponse2
35+
}
36+
};
37+
38+
// 3. Use the fake responses if those urls are attempted.
39+
var messageHandler = new FakeHttpMessageHandler(options);
40+
41+
var myService = new MyService(messageHandler);
42+
43+
// Act.
44+
// NOTE: network traffic will not leave your computer because you've faked the response, above.
45+
var result = await myService.GetAllDataAsync();
46+
47+
// Assert.
48+
result.Id.ShouldBe(69); // Returned from GetSomeFooDataAsync.
49+
result.Baa.FavMovie.ShouldBe("Star Wars - all of em"); // Returned from GetSomeBaaDataAsync.
50+
options[0].NumberOfTimesCalled.ShouldBe(1);
51+
options[1].NumberOfTimesCalled.ShouldBe(1);
52+
}
53+
}
54+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Newtonsoft.Json;
6+
using Shouldly;
7+
using WorldDomination.Net.Http;
8+
9+
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
10+
{
11+
public class MyService
12+
{
13+
public const string GetFooEndPoint = "http://www.something.com/some/website";
14+
public const string GetBaaEndPoint = "http://www.something.com/another/site";
15+
private readonly FakeHttpMessageHandler _messageHandler;
16+
17+
public MyService(FakeHttpMessageHandler messageHandler = null)
18+
{
19+
_messageHandler = messageHandler;
20+
}
21+
22+
public async Task<Foo> GetAllDataAsync()
23+
{
24+
var foo = await GetSomeFooDataAsync();
25+
foo.Baa = await GetSomeBaaDataAsync();
26+
27+
return foo;
28+
}
29+
30+
public async Task<Foo> GetSomeFooDataAsync()
31+
{
32+
return await GetSomeDataAsync<Foo>(GetFooEndPoint);
33+
}
34+
35+
public async Task<Baa> GetSomeBaaDataAsync()
36+
{
37+
// NOTE: notice how this request endpoint is different to the one, above?
38+
return await GetSomeDataAsync<Baa>(GetBaaEndPoint);
39+
}
40+
41+
private async Task<T> GetSomeDataAsync<T>(string endPoint)
42+
{
43+
endPoint.ShouldNotBeNullOrWhiteSpace();
44+
45+
HttpResponseMessage message;
46+
string content;
47+
using (var httpClient = _messageHandler == null
48+
? new System.Net.Http.HttpClient()
49+
: new System.Net.Http.HttpClient(_messageHandler))
50+
{
51+
message = await httpClient.GetAsync(endPoint);
52+
content = await message.Content.ReadAsStringAsync();
53+
}
54+
55+
if (message.StatusCode != HttpStatusCode.OK)
56+
{
57+
// TODO: handle this ru-roh-error.
58+
throw new InvalidOperationException(content);
59+
}
60+
61+
// Assumption: content is in a json format.
62+
return JsonConvert.DeserializeObject<T>(content);
63+
}
64+
}
65+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading.Tasks;
4+
using Shouldly;
5+
using WorldDomination.Net.Http;
6+
using Xunit;
7+
8+
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
9+
{
10+
public class SingleEndpointTests
11+
{
12+
[Theory]
13+
[InlineData(MyService.GetFooEndPoint)] // Specific url they are hitting.
14+
[InlineData("*")] // Don't care what url they are hitting.
15+
public async Task GivenSomeValidHttpRequest_GetSomeFooDataAsync_ReturnsAFoo(string endPoint)
16+
{
17+
// Arrange.
18+
const string responseData = "{ \"Id\":69, \"Name\":\"Jane\" }";
19+
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);
20+
21+
var options = new HttpMessageOptions
22+
{
23+
RequestUri = endPoint,
24+
HttpResponseMessage = messageResponse
25+
};
26+
27+
var messageHandler = new FakeHttpMessageHandler(options);
28+
29+
var myService = new MyService(messageHandler);
30+
31+
// Act.
32+
// NOTE: network traffic will not leave your computer because you've faked the response, above.
33+
var result = await myService.GetSomeFooDataAsync();
34+
35+
// Assert.
36+
result.Id.ShouldBe(69); // Returned from GetSomeFooDataAsync.
37+
result.Baa.ShouldBeNull();
38+
options.NumberOfTimesCalled.ShouldBe(1);
39+
}
40+
41+
[Fact]
42+
public async Task GivenADifferentFakeUrlEndpoint_GetSomeFooDataAsync_ThrowsAnException()
43+
{
44+
// Arrange.
45+
const string responseData = "{ \"Id\":69, \"Name\":\"Jane\" }";
46+
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);
47+
48+
var options = new HttpMessageOptions
49+
{
50+
RequestUri = "http://this.is.not.the.correct.endpoint",
51+
HttpResponseMessage = messageResponse
52+
};
53+
54+
var messageHandler = new FakeHttpMessageHandler(options);
55+
56+
var myService = new MyService(messageHandler);
57+
58+
// Act.
59+
// NOTE: network traffic will not leave your computer because you've faked the response, above.
60+
var result = await Should.ThrowAsync<InvalidOperationException>(myService.GetSomeFooDataAsync());
61+
62+
// Assert.
63+
result.Message.ShouldStartWith("No HttpResponseMessage found");
64+
}
65+
66+
[Fact]
67+
public async Task GivenAServerError_GetSomeFooDataAsync_ThrowsAnException()
68+
{
69+
// Arrange.
70+
const string responseData = "Something Blew Up";
71+
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData,
72+
HttpStatusCode.InternalServerError);
73+
74+
var options = new HttpMessageOptions
75+
{
76+
HttpResponseMessage = messageResponse
77+
};
78+
var messageHandler = new FakeHttpMessageHandler(options);
79+
80+
var myService = new MyService(messageHandler);
81+
82+
// Act.
83+
// NOTE: network traffic will not leave your computer because you've faked the response, above.
84+
var result = await Should.ThrowAsync<InvalidOperationException>(myService.GetSomeFooDataAsync());
85+
86+
// Assert.
87+
result.Message.ShouldStartWith(responseData);
88+
}
89+
}
90+
}

tests/HttpClient.Helpers.Tests/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +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="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
67
<package id="Shouldly" version="2.8.2" targetFramework="net45" />
78
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
89
<package id="xunit.core" version="2.1.0" targetFramework="net45" />

0 commit comments

Comments
 (0)