Skip to content

Commit

Permalink
Verifable (#12)
Browse files Browse the repository at this point in the history
- Fixes #2
- Number of times the request was called, now recorded.
- BREAKING CHANGE: Constructors were simplified to all use the same main `HttpMessageOptions` class.
  • Loading branch information
PureKrome authored Nov 21, 2016
1 parent e7413d3 commit 92a749a
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 68 deletions.
74 changes: 22 additions & 52 deletions src/HttpClient.Helpers/FakeMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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,22 +17,6 @@ public class FakeHttpMessageHandler : HttpClientHandler
private readonly IDictionary<string, HttpMessageOptions> _lotsOfOptions = new Dictionary<string, HttpMessageOptions>();


/// <summary>
/// A fake message handler.
/// </summary>
/// <remarks>This is mainly used for unit testing purposes.</remarks>
/// <param name="requestUri">The endpoint the HttpClient would normally try and connect to.</param>
/// <param name="httpResponseMessage">The faked response message.</param>
public FakeHttpMessageHandler(string requestUri,
HttpResponseMessage httpResponseMessage)
: this(new HttpMessageOptions
{
RequestUri = requestUri,
HttpResponseMessage = httpResponseMessage
})
{
}

/// <summary>
/// A fake message handler.
/// </summary>
Expand All @@ -58,47 +43,11 @@ public FakeHttpMessageHandler(HttpMessageOptions options) : this(new List<HttpMe
{
}

public FakeHttpMessageHandler(IDictionary<string, HttpResponseMessage> responses)
{
if (responses == null)
{
throw new ArgumentNullException(nameof(responses));
}

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

// NOTE: We assume HttpGet is the default when none are provided in this 'shortcut' method.
var lotsOfOptions = responses.Select(item => new HttpMessageOptions
{
RequestUri = item.Key,
HttpResponseMessage = item.Value,
HttpMethod = HttpMethod.Get
}).ToArray();

Initialize(lotsOfOptions);
}

public FakeHttpMessageHandler(IEnumerable<HttpMessageOptions> lotsOfOptions)
{
Initialize(lotsOfOptions.ToArray());
}

/// <summary>
/// A fake message handler which ignores whatever endpoint you're trying to connect to.
/// </summary>
/// <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>
/// <param name="httpResponseMessage">The faked response message.</param>
public FakeHttpMessageHandler(HttpResponseMessage httpResponseMessage)
: this(new HttpMessageOptions
{
HttpResponseMessage = httpResponseMessage
})
{
}

/// <summary>
/// A fake message handler for an exception.
/// </summary>
Expand Down Expand Up @@ -149,6 +98,9 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
throw new InvalidOperationException(errorMessage);
}

// Increment the number of times this option had been 'called'.
IncrementCalls(expectedOption);

tcs.SetResult(expectedOption.HttpResponseMessage);
return tcs.Task;
}
Expand Down Expand Up @@ -207,5 +159,23 @@ private HttpMessageOptions GetExpectedOption(HttpMessageOptions option)
(x.HttpContent == option.HttpContent ||
x.HttpContent == null));
}

private static void IncrementCalls(HttpMessageOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

var type = typeof(HttpMessageOptions);
var propertyInfo = type.GetProperty("NumberOfTimesCalled");
if (propertyInfo == null)
{
return;
}

var existingValue = (int) propertyInfo.GetValue(options);
propertyInfo.SetValue(options, ++existingValue);
}
}
}
2 changes: 2 additions & 0 deletions src/HttpClient.Helpers/HttpMessageOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public HttpContent HttpContent
}
}

public int NumberOfTimesCalled { get; private set; }

public override string ToString()
{
var httpMethodText = HttpMethod?.ToString() ?? NoValue;
Expand Down
89 changes: 73 additions & 16 deletions tests/HttpClient.Helpers.Tests/GetAsyncTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
Expand Down Expand Up @@ -119,72 +120,103 @@ public static IEnumerable<object[]> ValidSomeHttpMessageOptions

[Theory]
[MemberData(nameof(ValidHttpMessageOptions))]
public async Task GivenAnHttpMessageOptions_GetAsync_ReturnsAFakeResponse(HttpMessageOptions option)
public async Task GivenAnHttpMessageOptions_GetAsync_ReturnsAFakeResponse(HttpMessageOptions options)
{
var fakeHttpMessageHandler = new FakeHttpMessageHandler(option);
// Arrange.
var fakeHttpMessageHandler = new FakeHttpMessageHandler(options);

// Act & Assert.
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
options.NumberOfTimesCalled.ShouldBe(1);
}

[Theory]
[MemberData(nameof(ValidSomeHttpMessageOptions))]
public async Task GivenSomeHttpMessageOptions_GetAsync_ReturnsAFakeResponse(IEnumerable<HttpMessageOptions> lotsOfOption)
public async Task GivenSomeHttpMessageOptions_GetAsync_ReturnsAFakeResponse(IList<HttpMessageOptions> lotsOfOptions)
{
var fakeHttpMessageHandler = new FakeHttpMessageHandler(lotsOfOption);
// Arrange.
var fakeHttpMessageHandler = new FakeHttpMessageHandler(lotsOfOptions);

// Act & Assert.
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
lotsOfOptions.Sum(x => x.NumberOfTimesCalled).ShouldBe(1);
}

[Fact]
public async Task GivenAnHttpResponseMessage_GetAsync_ReturnsAFakeResponse()
{
// Arrange.
var httpResponseMessage = FakeHttpMessageHandler.GetStringHttpResponseMessage(ExpectedContent);
var fakeHttpMessageHandler = new FakeHttpMessageHandler(httpResponseMessage);
var options = new HttpMessageOptions
{
HttpResponseMessage = httpResponseMessage
};
var fakeHttpMessageHandler = new FakeHttpMessageHandler(options);

// Act & Assert.
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
options.NumberOfTimesCalled.ShouldBe(1);
}

[Fact]
public async Task GivenSomeHttpResponseMessages_GetAsync_ReturnsAFakeResponse()
{
const string requestUrl1 = RequestUri;
const string responseData1 = ExpectedContent;
var messageResponse1 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData1);
// Arrange.
var messageResponse1 = FakeHttpMessageHandler.GetStringHttpResponseMessage(ExpectedContent);

const string requestUrl2 = "http://www.something.com/another/site";
const string responseData2 = "Html, I am not.";
var messageResponse2 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData2);

const string requestUrl3 = "http://www.whatever.com/";
const string responseData3 = "<html><head><body>pew pew</body></head>";
var messageResponse3 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData3);

var messageResponses = new Dictionary<string, HttpResponseMessage>
var options = new List<HttpMessageOptions>
{
{requestUrl1, messageResponse1},
{requestUrl2, messageResponse2},
{requestUrl3, messageResponse3}
new HttpMessageOptions
{
RequestUri = RequestUri,
HttpResponseMessage = messageResponse1
},
new HttpMessageOptions
{
RequestUri = "http://www.something.com/another/site",
HttpResponseMessage = messageResponse2
},
new HttpMessageOptions
{
RequestUri = "http://www.whatever.com/",
HttpResponseMessage = messageResponse3
},
};

var fakeHttpMessageHandler = new FakeHttpMessageHandler(messageResponses);
var fakeHttpMessageHandler = new FakeHttpMessageHandler(options);

// Act & Assert.
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
options[0].NumberOfTimesCalled.ShouldBe(1);
options[1].NumberOfTimesCalled.ShouldBe(0);
options[2].NumberOfTimesCalled.ShouldBe(0);
}

[Fact]
public async Task GivenAnUnauthorisedStatusCodeResponse_GetAsync_ReturnsAFakeResponseWithAnUnauthorisedStatusCode()
{
// Arrange.
var messageResponse = FakeHttpMessageHandler.GetStringHttpResponseMessage("pew pew", HttpStatusCode.Unauthorized);
var messageHandler = new FakeHttpMessageHandler(RequestUri, messageResponse);
var options = new HttpMessageOptions
{
RequestUri = RequestUri,
HttpResponseMessage = messageResponse
};
var messageHandler = new FakeHttpMessageHandler(options);

HttpResponseMessage message;
using (var httpClient = new System.Net.Http.HttpClient(messageHandler))
Expand All @@ -195,6 +227,7 @@ public async Task GivenAnUnauthorisedStatusCodeResponse_GetAsync_ReturnsAFakeRes

// Assert.
message.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
options.NumberOfTimesCalled.ShouldBe(1);
}

[Fact]
Expand All @@ -217,6 +250,30 @@ public async Task GivenAValidHttpRequest_GetSomeDataAsync_ReturnsAFoo()
exception.Message.ShouldBe(errorMessage);
}

[Fact]
public async Task GivenAFewCallsToAnHttpRequest_GetSomeDataAsync_ReturnsAFakeResponse()
{
// Arrange.
var httpResponseMessage = FakeHttpMessageHandler.GetStringHttpResponseMessage(ExpectedContent);
var options = new HttpMessageOptions
{
HttpResponseMessage = httpResponseMessage
};
var fakeHttpMessageHandler = new FakeHttpMessageHandler(options);

// Act & Assert
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
await DoGetAsync(RequestUri,
ExpectedContent,
fakeHttpMessageHandler);
options.NumberOfTimesCalled.ShouldBe(3);
}

private static async Task DoGetAsync(string requestUri,
string expectedResponseContent,
FakeHttpMessageHandler fakeHttpMessageHandler)
Expand Down

0 comments on commit 92a749a

Please sign in to comment.