Unit Testing Class That Wraps HttpClient
I am in the process of writing unit tests for a new project we've created and one of the issues I've come across is how to properly unit test something that effectively wraps HttpClient. In this case, I have written a RestfulService class that exposes basic methods for calling a REST service from C#.
Here is the simple interface that the class implements:
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse?> Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
and here's is a trimmed down version of the implementation for example purposes:
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
/* boiler plate code for config and what have you */
private string Host => "http://localhost";
private NetworkCredential Credentials => new NetworkCredential("sampleUser", "samplePassword");
private string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private HttpClient Client => this.httpClient = this.httpClient ?? new HttpClient();
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
As you can see from the implementation it's a pretty thin wrapper that handles things like adding the auth headers (pulled from config) and some other small basic things.
My question: How can I mock the call to Client.SendAsync
to return predetermined responses to verify deserialization is happening properly and that the auth headers are added? Would it make more sense to move the adding of the auth headers out of DoRequest
and Mock the implementation of DoRequest
before running my test?
c# unit-testing xunit
|
show 1 more comment
I am in the process of writing unit tests for a new project we've created and one of the issues I've come across is how to properly unit test something that effectively wraps HttpClient. In this case, I have written a RestfulService class that exposes basic methods for calling a REST service from C#.
Here is the simple interface that the class implements:
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse?> Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
and here's is a trimmed down version of the implementation for example purposes:
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
/* boiler plate code for config and what have you */
private string Host => "http://localhost";
private NetworkCredential Credentials => new NetworkCredential("sampleUser", "samplePassword");
private string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private HttpClient Client => this.httpClient = this.httpClient ?? new HttpClient();
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
As you can see from the implementation it's a pretty thin wrapper that handles things like adding the auth headers (pulled from config) and some other small basic things.
My question: How can I mock the call to Client.SendAsync
to return predetermined responses to verify deserialization is happening properly and that the auth headers are added? Would it make more sense to move the adding of the auth headers out of DoRequest
and Mock the implementation of DoRequest
before running my test?
c# unit-testing xunit
2
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43
|
show 1 more comment
I am in the process of writing unit tests for a new project we've created and one of the issues I've come across is how to properly unit test something that effectively wraps HttpClient. In this case, I have written a RestfulService class that exposes basic methods for calling a REST service from C#.
Here is the simple interface that the class implements:
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse?> Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
and here's is a trimmed down version of the implementation for example purposes:
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
/* boiler plate code for config and what have you */
private string Host => "http://localhost";
private NetworkCredential Credentials => new NetworkCredential("sampleUser", "samplePassword");
private string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private HttpClient Client => this.httpClient = this.httpClient ?? new HttpClient();
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
As you can see from the implementation it's a pretty thin wrapper that handles things like adding the auth headers (pulled from config) and some other small basic things.
My question: How can I mock the call to Client.SendAsync
to return predetermined responses to verify deserialization is happening properly and that the auth headers are added? Would it make more sense to move the adding of the auth headers out of DoRequest
and Mock the implementation of DoRequest
before running my test?
c# unit-testing xunit
I am in the process of writing unit tests for a new project we've created and one of the issues I've come across is how to properly unit test something that effectively wraps HttpClient. In this case, I have written a RestfulService class that exposes basic methods for calling a REST service from C#.
Here is the simple interface that the class implements:
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse?> Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
and here's is a trimmed down version of the implementation for example purposes:
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
/* boiler plate code for config and what have you */
private string Host => "http://localhost";
private NetworkCredential Credentials => new NetworkCredential("sampleUser", "samplePassword");
private string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private HttpClient Client => this.httpClient = this.httpClient ?? new HttpClient();
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
As you can see from the implementation it's a pretty thin wrapper that handles things like adding the auth headers (pulled from config) and some other small basic things.
My question: How can I mock the call to Client.SendAsync
to return predetermined responses to verify deserialization is happening properly and that the auth headers are added? Would it make more sense to move the adding of the auth headers out of DoRequest
and Mock the implementation of DoRequest
before running my test?
c# unit-testing xunit
c# unit-testing xunit
asked Nov 19 '18 at 20:43
Adam HAdam H
972115
972115
2
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43
|
show 1 more comment
2
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43
2
2
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43
|
show 1 more comment
1 Answer
1
active
oldest
votes
I was able to figure this out using an accessor for the HttpClient and then mocking out the HttpMessageHandler. Here is the code I used.
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
And here is the implementation for test cases:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
This allows me to pass the desired response into SetupRestfulService
along with credentials and get back an object I can call my functions on. It's a little less than Ideal but it saves me from having to flesh out the entire adapter pattern and going down that rabbit hole.
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53382348%2funit-testing-class-that-wraps-httpclient%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I was able to figure this out using an accessor for the HttpClient and then mocking out the HttpMessageHandler. Here is the code I used.
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
And here is the implementation for test cases:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
This allows me to pass the desired response into SetupRestfulService
along with credentials and get back an object I can call my functions on. It's a little less than Ideal but it saves me from having to flesh out the entire adapter pattern and going down that rabbit hole.
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
add a comment |
I was able to figure this out using an accessor for the HttpClient and then mocking out the HttpMessageHandler. Here is the code I used.
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
And here is the implementation for test cases:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
This allows me to pass the desired response into SetupRestfulService
along with credentials and get back an object I can call my functions on. It's a little less than Ideal but it saves me from having to flesh out the entire adapter pattern and going down that rabbit hole.
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
add a comment |
I was able to figure this out using an accessor for the HttpClient and then mocking out the HttpMessageHandler. Here is the code I used.
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
And here is the implementation for test cases:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
This allows me to pass the desired response into SetupRestfulService
along with credentials and get back an object I can call my functions on. It's a little less than Ideal but it saves me from having to flesh out the entire adapter pattern and going down that rabbit hole.
I was able to figure this out using an accessor for the HttpClient and then mocking out the HttpMessageHandler. Here is the code I used.
public interface IHttpClientAccessor
{
HttpClient HttpClient
{
get;
}
}
public class HttpClientAccessor : IHttpClientAccessor
{
public HttpClientAccessor()
{
this.HttpClient = new HttpClient();
}
public HttpClient HttpClient
{
get;
}
}
public interface IRestfulService
{
Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null);
Task<T> Post<T>(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Put(string url, IDictionary<string, string> parameters, object bodyObject, IDictionary<string, string> headers = null);
Task<string> Delete(string url, object bodyObject, IDictionary<string, string> headers = null);
Task<FileResponse? > Download(string url, IDictionary<string, string> urlParams = null, IDictionary<string, string> headers = null);
}
public class RestfulService : IRestfulService
{
private HttpClient httpClient = null;
private NetworkCredential credentials = null;
private IHttpClientAccessor httpClientAccessor;
public RestfulService(IConfigurationService configurationService, IHttpClientAccessor httpClientAccessor)
{
this.ConfigurationService = configurationService;
this.httpClientAccessor = httpClientAccessor;
}
public string AuthHeader
{
get
{
if (this.Credentials != null)
{
return string.Format("Basic {0}", Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)));
}
else
{
return string.Empty;
}
}
}
private IConfigurationService ConfigurationService
{
get;
}
private string Host => "http://locahost/";
private NetworkCredential Credentials => this.credentials ?? new NetworkCredential("someUser", "somePassword");
private HttpClient Client => this.httpClient = this.httpClient ?? this.httpClientAccessor.HttpClient;
public async Task<T> Get<T>(string url, IDictionary<string, string> parameters, IDictionary<string, string> headers = null)
{
var result = await this.DoRequest(url, HttpMethod.Get, parameters, null, headers);
if (typeof (T) == typeof (string))
{
return (T)(object)result;
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
}
}
private async Task<string> DoRequest(string url, HttpMethod method, IDictionary<string, string> urlParams = null, object bodyObject = null, IDictionary<string, string> headers = null)
{
string fullRequestUrl = string.Empty;
HttpResponseMessage response = null;
if (headers == null)
{
headers = new Dictionary<string, string>();
}
if (this.Credentials != null)
{
headers.Add("Authorization", this.AuthHeader);
}
headers.Add("Accept", "application/json");
fullRequestUrl = string.Format("{0}{1}{2}", this.Host.ToString(), url, urlParams?.ToQueryString());
using (var request = new HttpRequestMessage(method, fullRequestUrl))
{
request.AddHeaders(headers);
if (bodyObject != null)
{
request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(bodyObject), System.Text.Encoding.UTF8, "application/json");
}
response = await this.Client.SendAsync(request).ConfigureAwait(false);
}
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errDesc = response.ReasonPhrase;
if (!string.IsNullOrEmpty(content))
{
errDesc += " - " + content;
}
throw new HttpRequestException(string.Format("RestfulService: Error sending request to web service URL {0}. Reason: {1}", fullRequestUrl, errDesc));
}
return content;
}
}
And here is the implementation for test cases:
private RestfulService SetupRestfulService(HttpResponseMessage returns, string userName = "notARealUser", string password = "notARealPassword")
{
var mockHttpAccessor = new Mock<IHttpClientAccessor>();
var mockHttpHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testServiceEndpoints = Options.Create<Configuration.ServiceEndpoints>(new Configuration.ServiceEndpoints()
{OneEndPoint = "http://localhost/test", AnotherEndPoint = "http://localhost/test"});
var testAuth = Options.Create<AuthOptions>(new AuthOptions()
{Password = password, Username = userName});
mockHttpHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(returns).Verifiable();
mockHttpAccessor.SetupGet(p => p.HttpClient).Returns(new HttpClient(mockHttpHandler.Object));
return new RestfulService(new ConfigurationService(testServiceEndpoints, testAuth), mockHttpAccessor.Object);
}
[Fact]
public void TestAuthorizationHeader()
{
// notARealUser : notARealPassword
var expected = "Basic bm90QVJlYWxVc2VyOm5vdEFSZWFsUGFzc3dvcmQ=";
var service = this.SetupRestfulService(new HttpResponseMessage{StatusCode = HttpStatusCode.OK, Content = new StringContent("AuthorizationTest")});
Assert.Equal(expected, service.AuthHeader);
}
[Fact]
public async Task TestGetPlainString()
{
var service = this.SetupRestfulService(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("test") });
var result = await service.Get<string>("test", null, null);
Assert.Equal("test", result);
}
This allows me to pass the desired response into SetupRestfulService
along with credentials and get back an object I can call my functions on. It's a little less than Ideal but it saves me from having to flesh out the entire adapter pattern and going down that rabbit hole.
edited Nov 20 '18 at 15:37
answered Nov 20 '18 at 15:20
Adam HAdam H
972115
972115
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
add a comment |
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
1
1
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
Similar to what I did here stackoverflow.com/a/45154289/5233410
– Nkosi
Nov 20 '18 at 15:41
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
@Nkosi wow, this is almost like bumping into a celebrity! I definitely read your posts on a couple of threads and armed with that info I hacked this together. I didn't get a chance in those threads to say it so I'll say it here, Thank You for your help.
– Adam H
Nov 20 '18 at 15:44
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
Glad to help. Happy Coding.
– Nkosi
Nov 20 '18 at 15:47
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53382348%2funit-testing-class-that-wraps-httpclient%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Mocking HttpClient is Hard. See also.
– Robert Harvey♦
Nov 19 '18 at 20:45
@RobertHarvey Thanks for the link, I'm going to read up on that now.
– Adam H
Nov 19 '18 at 20:47
This should be tested against actual services by using actual HttpClient.
– Fabio
Nov 19 '18 at 23:24
@Fabio thanks for the reply, however, testing against the actual services that it is calling would be a regression test and beyond the scope of a unit test. I have found a way to accomplish what I need and will be posting an answer to this question shortly.
– Adam H
Nov 20 '18 at 15:10
Have you considered using Flurl? It's a thin wrapper above HttpClient. It has a great syntax, and is testable.
– mason
Nov 20 '18 at 15:43