Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ CLI: `install-package WorldDomination.HttpClient.Helpers`
## Sample Code

There's [plenty more examples](https://github.com/PureKrome/HttpClient.Helpers/wiki) about to wire up:
- [A really simple example](https://github.com/PureKrome/HttpClient.Helpers/wiki/Constructor-Injection)
- [A really simple example](https://github.com/PureKrome/HttpClient.Helpers/wiki/A-single-endpoint)
- [Multiple endpoints](https://github.com/PureKrome/HttpClient.Helpers/wiki/Multiple-endpoints) at once
- [Wildcard endpoints](https://github.com/PureKrome/HttpClient.Helpers/wiki/Wildcard-endpoints)
- [Throwing exceptions](https://github.com/PureKrome/HttpClient.Helpers/wiki/Faking-an-Exception) and handling it
Expand Down
23 changes: 0 additions & 23 deletions src/HttpClient.Helpers/FakeMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -16,26 +15,10 @@ public class FakeHttpMessageHandler : HttpClientHandler

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


/// <summary>
/// A fake message handler.
/// </summary>
/// <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>
///// <param name="httpResponseMessages">A dictionary of request endpoints and their respective fake response message.</param>
//public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> httpResponseMessages)
//{
// if (httpResponseMessages == null)
// {
// throw new ArgumentNullException(nameof(httpResponseMessages));
// }

// if (!httpResponseMessages.Any())
// {
// throw new ArgumentOutOfRangeException(nameof(httpResponseMessages));
// }

// _responses = httpResponseMessages;
//}
public FakeHttpMessageHandler(HttpMessageOptions options) : this(new List<HttpMessageOptions>
{
options
Expand Down Expand Up @@ -63,12 +46,6 @@ public FakeHttpMessageHandler(HttpRequestException exception)
_exception = exception;
}

/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
Expand Down
3 changes: 3 additions & 0 deletions src/HttpClient.Helpers/HttpMessageOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public HttpContent HttpContent
}
}

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

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Shouldly, Version=2.8.2.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
<HintPath>..\..\packages\Shouldly.2.8.2\lib\net40\Shouldly.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -63,10 +67,15 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Wiki Examples\Baa.cs" />
<Compile Include="Wiki Examples\Foo.cs" />
<Compile Include="Wiki Examples\MultipleEndpointTests.cs" />
<Compile Include="GetAsyncTests.cs" />
<Compile Include="GetStringAsyncTests.cs" />
<Compile Include="PostAsyncTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Wiki Examples\MyService.cs" />
<Compile Include="Wiki Examples\SingleEndpointTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
8 changes: 8 additions & 0 deletions tests/HttpClient.Helpers.Tests/Wiki Examples/Baa.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
{
public class Baa
{
public string FavGame { get; set; }
public string FavMovie { get; set; }
}
}
9 changes: 9 additions & 0 deletions tests/HttpClient.Helpers.Tests/Wiki Examples/Foo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
{
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public Baa Baa { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Threading.Tasks;
using Shouldly;
using WorldDomination.Net.Http;
using Xunit;

namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
{
public class MultipleEndpointTests
{
[Fact]
public async Task GivenSomeValidHttpRequests_GetSomeDataAsync_ReturnsAFoo()
{
// Arrange.

// 1. First fake response.
const string responseData1 = "{ \"Id\":69, \"Name\":\"Jane\" }";
var messageResponse1 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData1);

// 2. Second fake response.
const string responseData2 = "{ \"FavGame\":\"Star Wars\", \"FavMovie\":\"Star Wars - all of em\" }";
var messageResponse2 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData2);

// Prepare our 'options' with all of the above fake stuff.
var options = new[]
{
new HttpMessageOptions
{
RequestUri = MyService.GetFooEndPoint,
HttpResponseMessage = messageResponse1
},
new HttpMessageOptions
{
RequestUri = MyService.GetBaaEndPoint,
HttpResponseMessage = messageResponse2
}
};

// 3. Use the fake responses if those urls are attempted.
var messageHandler = new FakeHttpMessageHandler(options);

var myService = new MyService(messageHandler);

// Act.
// NOTE: network traffic will not leave your computer because you've faked the response, above.
var result = await myService.GetAllDataAsync();

// Assert.
result.Id.ShouldBe(69); // Returned from GetSomeFooDataAsync.
result.Baa.FavMovie.ShouldBe("Star Wars - all of em"); // Returned from GetSomeBaaDataAsync.
options[0].NumberOfTimesCalled.ShouldBe(1);
options[1].NumberOfTimesCalled.ShouldBe(1);
}
}
}
65 changes: 65 additions & 0 deletions tests/HttpClient.Helpers.Tests/Wiki Examples/MyService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Shouldly;
using WorldDomination.Net.Http;

namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
{
public class MyService
{
public const string GetFooEndPoint = "http://www.something.com/some/website";
public const string GetBaaEndPoint = "http://www.something.com/another/site";
private readonly FakeHttpMessageHandler _messageHandler;

public MyService(FakeHttpMessageHandler messageHandler = null)
{
_messageHandler = messageHandler;
}

public async Task<Foo> GetAllDataAsync()
{
var foo = await GetSomeFooDataAsync();
foo.Baa = await GetSomeBaaDataAsync();

return foo;
}

public async Task<Foo> GetSomeFooDataAsync()
{
return await GetSomeDataAsync<Foo>(GetFooEndPoint);
}

public async Task<Baa> GetSomeBaaDataAsync()
{
// NOTE: notice how this request endpoint is different to the one, above?
return await GetSomeDataAsync<Baa>(GetBaaEndPoint);
}

private async Task<T> GetSomeDataAsync<T>(string endPoint)
{
endPoint.ShouldNotBeNullOrWhiteSpace();

HttpResponseMessage message;
string content;
using (var httpClient = _messageHandler == null
? new System.Net.Http.HttpClient()
: new System.Net.Http.HttpClient(_messageHandler))
{
message = await httpClient.GetAsync(endPoint);
content = await message.Content.ReadAsStringAsync();
}

if (message.StatusCode != HttpStatusCode.OK)
{
// TODO: handle this ru-roh-error.
throw new InvalidOperationException(content);
}

// Assumption: content is in a json format.
return JsonConvert.DeserializeObject<T>(content);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Shouldly;
using WorldDomination.Net.Http;
using Xunit;

namespace WorldDomination.HttpClient.Helpers.Tests.Wiki_Examples
{
public class SingleEndpointTests
{
[Theory]
[InlineData(MyService.GetFooEndPoint)] // Specific url they are hitting.
[InlineData("*")] // Don't care what url they are hitting.
public async Task GivenSomeValidHttpRequest_GetSomeFooDataAsync_ReturnsAFoo(string endPoint)
{
// Arrange.
const string responseData = "{ \"Id\":69, \"Name\":\"Jane\" }";
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);

var options = new HttpMessageOptions
{
RequestUri = endPoint,
HttpResponseMessage = messageResponse
};

var messageHandler = new FakeHttpMessageHandler(options);

var myService = new MyService(messageHandler);

// Act.
// NOTE: network traffic will not leave your computer because you've faked the response, above.
var result = await myService.GetSomeFooDataAsync();

// Assert.
result.Id.ShouldBe(69); // Returned from GetSomeFooDataAsync.
result.Baa.ShouldBeNull();
options.NumberOfTimesCalled.ShouldBe(1);
}

[Fact]
public async Task GivenADifferentFakeUrlEndpoint_GetSomeFooDataAsync_ThrowsAnException()
{
// Arrange.
const string responseData = "{ \"Id\":69, \"Name\":\"Jane\" }";
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData);

var options = new HttpMessageOptions
{
RequestUri = "http://this.is.not.the.correct.endpoint",
HttpResponseMessage = messageResponse
};

var messageHandler = new FakeHttpMessageHandler(options);

var myService = new MyService(messageHandler);

// Act.
// NOTE: network traffic will not leave your computer because you've faked the response, above.
var result = await Should.ThrowAsync<InvalidOperationException>(myService.GetSomeFooDataAsync());

// Assert.
result.Message.ShouldStartWith("No HttpResponseMessage found");
}

[Fact]
public async Task GivenAServerError_GetSomeFooDataAsync_ThrowsAnException()
{
// Arrange.
const string responseData = "Something Blew Up";
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData,
HttpStatusCode.InternalServerError);

var options = new HttpMessageOptions
{
HttpResponseMessage = messageResponse
};
var messageHandler = new FakeHttpMessageHandler(options);

var myService = new MyService(messageHandler);

// Act.
// NOTE: network traffic will not leave your computer because you've faked the response, above.
var result = await Should.ThrowAsync<InvalidOperationException>(myService.GetSomeFooDataAsync());

// Assert.
result.Message.ShouldStartWith(responseData);
}
}
}
1 change: 1 addition & 0 deletions tests/HttpClient.Helpers.Tests/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="Shouldly" version="2.8.2" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
<package id="xunit.core" version="2.1.0" targetFramework="net45" />
Expand Down