From 69b3f6076ac811cf25f8d4159bd55f0c0eeb512f Mon Sep 17 00:00:00 2001 From: averageprogrammer Date: Sat, 21 Oct 2017 01:11:06 -0400 Subject: [PATCH] Implemented a mock http client Put the httpclient reference behind a interface to add the ability for deterministic unit testing of api interactions in the future. --- RiotSharp/Http/Interfaces/IRiotApiClient.cs | 30 ++++++++++++++++ RiotSharp/Http/MockRiotApiClient.cs | 39 +++++++++++++++++++++ RiotSharp/Http/RateLimitedRequester.cs | 5 +++ RiotSharp/Http/Requester.cs | 4 +++ RiotSharp/Http/RequesterBase.cs | 26 +++++++++----- RiotSharp/Http/RiotApiClient.cs | 31 ++++++++++++++++ 6 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 RiotSharp/Http/Interfaces/IRiotApiClient.cs create mode 100644 RiotSharp/Http/MockRiotApiClient.cs create mode 100644 RiotSharp/Http/RiotApiClient.cs diff --git a/RiotSharp/Http/Interfaces/IRiotApiClient.cs b/RiotSharp/Http/Interfaces/IRiotApiClient.cs new file mode 100644 index 00000000..2039d125 --- /dev/null +++ b/RiotSharp/Http/Interfaces/IRiotApiClient.cs @@ -0,0 +1,30 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace RiotSharp.Http.Interfaces +{ + public interface IRiotApiClient + { + /// + /// Send a get request asynchronously to the riot api. + /// + /// + /// + Task GetAsync(Uri requestUri); + /// + /// Send a put request asynchronously to the riot api. + /// + /// + /// + /// + Task PutAsync(Uri requestUri, HttpContent content); + /// + /// Send a post request asynchronously to the riot api. + /// + /// + /// + /// + Task PostAsync(Uri requestUri, HttpContent content); + } +} diff --git a/RiotSharp/Http/MockRiotApiClient.cs b/RiotSharp/Http/MockRiotApiClient.cs new file mode 100644 index 00000000..608338e2 --- /dev/null +++ b/RiotSharp/Http/MockRiotApiClient.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using RiotSharp.Http.Interfaces; + +namespace RiotSharp.Http +{ + public class MockRiotApiClient : IRiotApiClient + { + private readonly Dictionary responses; + + public MockRiotApiClient() + { + responses = new Dictionary(); + } + + public MockRiotApiClient(Dictionary mockResponses) + { + responses = mockResponses; + } + + public Task GetAsync(Uri requestUri) + { + return Task.FromResult(responses.ContainsKey(requestUri) ? responses[requestUri] : new HttpResponseMessage(HttpStatusCode.NotFound)); + } + + public Task PutAsync(Uri requestUri, HttpContent content) + { + return Task.FromResult(responses.ContainsKey(requestUri) ? responses[requestUri] : new HttpResponseMessage(HttpStatusCode.NotFound)); + } + + public Task PostAsync(Uri requestUri, HttpContent content) + { + return Task.FromResult(responses.ContainsKey(requestUri) ? responses[requestUri] : new HttpResponseMessage(HttpStatusCode.NotFound)); + } + } +} diff --git a/RiotSharp/Http/RateLimitedRequester.cs b/RiotSharp/Http/RateLimitedRequester.cs index eabe41a7..a8f0b337 100644 --- a/RiotSharp/Http/RateLimitedRequester.cs +++ b/RiotSharp/Http/RateLimitedRequester.cs @@ -20,6 +20,11 @@ public RateLimitedRequester(string apiKey, IDictionary rateLimits RateLimits = rateLimits; } + public RateLimitedRequester(string apiKey, IDictionary rateLimits, IRiotApiClient apiClient) : base(apiKey, apiClient) + { + RateLimits = rateLimits; + } + private readonly Dictionary rateLimiters = new Dictionary(); #region Public Methods diff --git a/RiotSharp/Http/Requester.cs b/RiotSharp/Http/Requester.cs index 999a30b2..9ca51290 100644 --- a/RiotSharp/Http/Requester.cs +++ b/RiotSharp/Http/Requester.cs @@ -18,6 +18,10 @@ public Requester(string apiKey) : base(apiKey) { } + public Requester(string apiKey, IRiotApiClient apiClient) : base(apiKey, apiClient) + { + } + #region Public Methods public string CreateGetRequest(string relativeUrl, Region region, List addedArguments = null, bool useHttps = true) diff --git a/RiotSharp/Http/RequesterBase.cs b/RiotSharp/Http/RequesterBase.cs index 80486e6a..c0fce12b 100644 --- a/RiotSharp/Http/RequesterBase.cs +++ b/RiotSharp/Http/RequesterBase.cs @@ -5,6 +5,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using RiotSharp.Http.Interfaces; namespace RiotSharp.Http { @@ -12,8 +13,7 @@ public abstract class RequesterBase { protected string rootDomain; protected const string platformDomain = ".api.riotgames.com"; - private readonly HttpClient httpClient; - + private readonly IRiotApiClient riotApiClient; public string ApiKey { get; set; } protected RequesterBase(string apiKey) @@ -21,7 +21,15 @@ protected RequesterBase(string apiKey) if (string.IsNullOrWhiteSpace(apiKey)) throw new ArgumentNullException(nameof(apiKey)); ApiKey = apiKey; - httpClient = new HttpClient(); + riotApiClient = new RiotApiClient(); + } + + protected RequesterBase(string apiKey, IRiotApiClient apiClient) + { + if (string.IsNullOrWhiteSpace(apiKey)) + throw new ArgumentNullException(nameof(apiKey)); + ApiKey = apiKey; + riotApiClient = apiClient; } #region Protected Methods @@ -34,7 +42,7 @@ protected RequesterBase(string apiKey) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected HttpResponseMessage Get(HttpRequestMessage request) { - var response = httpClient.GetAsync(request.RequestUri).Result; + var response = riotApiClient.GetAsync(request.RequestUri).Result; if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); @@ -50,7 +58,7 @@ protected HttpResponseMessage Get(HttpRequestMessage request) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected async Task GetAsync(HttpRequestMessage request) { - var response = await httpClient.GetAsync(request.RequestUri); + var response = await riotApiClient.GetAsync(request.RequestUri); if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); @@ -67,7 +75,7 @@ protected async Task GetAsync(HttpRequestMessage request) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected HttpResponseMessage Put(HttpRequestMessage request) { - var response = httpClient.PutAsync(request.RequestUri, request.Content).Result; + var response = riotApiClient.PutAsync(request.RequestUri, request.Content).Result; if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); @@ -83,7 +91,7 @@ protected HttpResponseMessage Put(HttpRequestMessage request) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected async Task PutAsync(HttpRequestMessage request) { - var response = await httpClient.PutAsync(request.RequestUri, request.Content); + var response = await riotApiClient.PutAsync(request.RequestUri, request.Content); if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); @@ -99,7 +107,7 @@ protected async Task PutAsync(HttpRequestMessage request) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected HttpResponseMessage Post(HttpRequestMessage request) { - var response = httpClient.PostAsync(request.RequestUri, request.Content).Result; + var response = riotApiClient.PostAsync(request.RequestUri, request.Content).Result; if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); @@ -115,7 +123,7 @@ protected HttpResponseMessage Post(HttpRequestMessage request) /// Thrown if an Http error occurs. Contains the Http error code and error message. protected async Task PostAsync(HttpRequestMessage request) { - var response = await httpClient.PostAsync(request.RequestUri, request.Content); + var response = await riotApiClient.PostAsync(request.RequestUri, request.Content); if (!response.IsSuccessStatusCode) { HandleRequestFailure(response.StatusCode); diff --git a/RiotSharp/Http/RiotApiClient.cs b/RiotSharp/Http/RiotApiClient.cs new file mode 100644 index 00000000..a682e834 --- /dev/null +++ b/RiotSharp/Http/RiotApiClient.cs @@ -0,0 +1,31 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using RiotSharp.Http.Interfaces; + +namespace RiotSharp.Http +{ + public class RiotApiClient : IRiotApiClient + { + private readonly HttpClient httpClient; + + public RiotApiClient() + { + httpClient = new HttpClient(); + } + public Task GetAsync(Uri requestUri) + { + return httpClient.GetAsync(requestUri); + } + + public Task PutAsync(Uri requestUri, HttpContent content) + { + return httpClient.PutAsync(requestUri, content); + } + + public Task PostAsync(Uri requestUri, HttpContent content) + { + return httpClient.PostAsync(requestUri, content); + } + } +}