diff --git a/src/HttpClient.Helpers/FakeMessageHandler.cs b/src/HttpClient.Helpers/FakeMessageHandler.cs index 6ffcf34..b412b02 100644 --- a/src/HttpClient.Helpers/FakeMessageHandler.cs +++ b/src/HttpClient.Helpers/FakeMessageHandler.cs @@ -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; @@ -16,22 +17,6 @@ public class FakeHttpMessageHandler : HttpClientHandler private readonly IDictionary _lotsOfOptions = new Dictionary(); - /// - /// A fake message handler. - /// - /// This is mainly used for unit testing purposes. - /// The endpoint the HttpClient would normally try and connect to. - /// The faked response message. - public FakeHttpMessageHandler(string requestUri, - HttpResponseMessage httpResponseMessage) - : this(new HttpMessageOptions - { - RequestUri = requestUri, - HttpResponseMessage = httpResponseMessage - }) - { - } - /// /// A fake message handler. /// @@ -58,47 +43,11 @@ public FakeHttpMessageHandler(HttpMessageOptions options) : this(new List 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 lotsOfOptions) { Initialize(lotsOfOptions.ToArray()); } - /// - /// A fake message handler which ignores whatever endpoint you're trying to connect to. - /// - /// 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. - /// The faked response message. - public FakeHttpMessageHandler(HttpResponseMessage httpResponseMessage) - : this(new HttpMessageOptions - { - HttpResponseMessage = httpResponseMessage - }) - { - } - /// /// A fake message handler for an exception. /// @@ -149,6 +98,9 @@ protected override Task 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; } @@ -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); + } } } \ No newline at end of file diff --git a/src/HttpClient.Helpers/HttpMessageOptions.cs b/src/HttpClient.Helpers/HttpMessageOptions.cs index 263de96..853ef33 100644 --- a/src/HttpClient.Helpers/HttpMessageOptions.cs +++ b/src/HttpClient.Helpers/HttpMessageOptions.cs @@ -51,6 +51,8 @@ public HttpContent HttpContent } } + public int NumberOfTimesCalled { get; private set; } + public override string ToString() { var httpMethodText = HttpMethod?.ToString() ?? NoValue; diff --git a/tests/HttpClient.Helpers.Tests/GetAsyncTests.cs b/tests/HttpClient.Helpers.Tests/GetAsyncTests.cs index 009600c..9eaa6b7 100644 --- a/tests/HttpClient.Helpers.Tests/GetAsyncTests.cs +++ b/tests/HttpClient.Helpers.Tests/GetAsyncTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -119,64 +120,90 @@ public static IEnumerable 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 lotsOfOption) + public async Task GivenSomeHttpMessageOptions_GetAsync_ReturnsAFakeResponse(IList 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 = "pew pew"; var messageResponse3 = FakeHttpMessageHandler.GetStringHttpResponseMessage(responseData3); - var messageResponses = new Dictionary + var options = new List { - {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] @@ -184,7 +211,12 @@ public async Task GivenAnUnauthorisedStatusCodeResponse_GetAsync_ReturnsAFakeRes { // 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)) @@ -195,6 +227,7 @@ public async Task GivenAnUnauthorisedStatusCodeResponse_GetAsync_ReturnsAFakeRes // Assert. message.StatusCode.ShouldBe(HttpStatusCode.Unauthorized); + options.NumberOfTimesCalled.ShouldBe(1); } [Fact] @@ -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)