How to Mock HttpClient using Substitute - c#

I am trying to use NSubstitute to mock HttpClient. Here's the code:
public static HttpClient GetHttpClient(bool isSucess = true, string methodType = "GET")
{
var mockIHttpMessageHandler = Substitute.For<IMockHttpMessageHandler>();
var mockHttpMessageHandler = Substitute.For<MockHttpMessageHandler>(mockIHttpMessageHandler);
var httpResponse = Substitute.For<HttpResponseMessage>();
httpResponse.Content = new StringContent("\"test\"");
if (isSucess)
httpResponse.StatusCode = HttpStatusCode.OK;
else
httpResponse.StatusCode = HttpStatusCode.NotFound;
var mockHttpClient = Substitute.For<HttpClient>(mockHttpMessageHandler);
mockHttpClient.BaseAddress = new Uri("http://localhost");
if(methodType != "POST"){
mockHttpClient.GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
}
return mockHttpClient;
}
However, I got an error at this line:
mockHttpClient.GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
And the error is
NSubstitute.Exceptions.RedundantArgumentMatcherException: 'Some
argument specifications (e.g. Arg.Is, Arg.Any) were left over after
the last call.
This is often caused by using an argument spec with a call to a member
NSubstitute does not handle (such as a non-virtual member or a call to
an instance which is not a substitute), or for a purpose other than
specifying a call (such as using an arg spec as a return value). For
example:
var sub = Substitute.For<SomeClass>();
var realType = new MyRealType(sub);
// INCORRECT, arg spec used on realType, not a substitute:
realType.SomeMethod(Arg.Any<int>()).Returns(2);
// INCORRECT, arg spec used as a return value, not to specify a call:
sub.VirtualMethod(2).Returns(Arg.Any<int>());
// INCORRECT, arg spec used with a non-virtual method:
sub.NonVirtualMethod(Arg.Any<int>()).Returns(2);
// CORRECT, arg spec used to specify virtual call on a substitute:
sub.VirtualMethod(Arg.Any<int>()).Returns(2);
To fix this make sure you only use argument specifications with calls
to substitutes. If your substitute is a class, make sure the member is
virtual.
Another possible cause is that the argument spec type does not match
the actual argument type, but code compiles due to an implicit cast.
For example, Arg.Any() was used, but Arg.Any() was
required.
NOTE: the cause of this exception can be in a previously executed
test. Use the diagnostics below to see the types of any redundant arg
specs, then work out where they are being created.
Diagnostic information:
Remaining (non-bound) argument specifications:
any Uri
All argument specifications:
any Uri
Are they suggesting I need to change the getAsync method? There's no virtual method for GetAsync
Edit:
I have also tried to remove NSubstitute for HttpClient as follows, but I still got the same error:
public static HttpClient GetHttpClient(bool isSucess = true, string methodType = "GET")
{
var mockIHttpMessageHandler = Substitute.For<IMockHttpMessageHandler>();
var mockHttpMessageHandler = Substitute.For<MockHttpMessageHandler>(mockIHttpMessageHandler);
var httpResponse = Substitute.For<HttpResponseMessage>();
httpResponse.Content = new StringContent("\"test\"");
if (isSucess)
httpResponse.StatusCode = HttpStatusCode.OK;
else
httpResponse.StatusCode = HttpStatusCode.NotFound;
var httpClient = new HttpClient(mockHttpMessageHandler);
httpClient = new Uri("http://localhost");
if(methodType != "POST"){
httpClient .GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
}
return httpClient
}

I appreciate this is an old(ish) question, but it's at the top of the Google Search results for "c# mock httpclient using nsubstitute" today, so I figured an answer would be useful.
First we need to create a mock implemenation of HttpMessageHandler. As you can see, we're overriding the protected SendAsync() method and exposing it's body via our public Send() method.
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Send(request, cancellationToken);
}
public virtual Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
Next we need to set up our mocks. Note that I'm using Substitute.ForPartsOf<T> instead of Substitute.For<T>.
var mockHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
var httpClient = new HttpClient(mockHttpMessageHandler);
Finally, we can now use NSubstitute to intercept the call to Send() on our handler, which is called by the HttpClient for every request, and return our mocked HttpResponseMessage back via the client.
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK);
mockHttpMessageHandler.Send(Arg.Any<AnyHttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(mockResponse);
var result = await httpClient.GetAsync<string>("https://tempuri.org");
Edit for .NET 6
As .NET 6 introduces a protected virtual Send() method to the HttpMessageHandler class (which will also need overriding if you're using the synchronous HttpClient calls), some modifications are required to our MockHttpMessageHandler:
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(MockSend(request, cancellationToken));
}
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
return MockSend(request, cancellationToken);
}
public virtual HttpResponseMessage MockSend(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}

Related

FakeItEasy - Is it possible to test constraints asynchronously (i.e. MatchesAsync)?

I've run into difficulty testing System.Net.Http.HttpClient with FakeItEasy. Consider this scenario:
//Service that consumes HttpClient
public class LoggingService
{
private readonly HttpClient _client;
public LoggingService(HttpClient client)
{
_client = client;
_client.BaseAddress = new Uri("http://www.example.com");
}
public async Task Log(LogEntry logEntry)
{
var json = JsonConvert.SerializeObject(logEntry);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
await _client.PostAsync("/api/logging", httpContent);
}
}
public class LogEntry
{
public string MessageText { get; set; }
public DateTime DateLogged { get; set; }
}
Unit Testing
From a unit testing perspective, I want to verify that HttpClient posts the specified logEntry payload to the appropriate URL (http://www.example.com/api/logging). (Side Note: I can't test the HttpClient.PostAsync() method directly because my service uses the concrete implementation of HttpClient and Microsoft does not provide an interface for it. However, I can create my own HttpClient that uses a FakeMessageHandler (below) as a dependency, and inject that into the service for testing purposes. From there, I can test DoSendAsync()
//Helper class for mocking the MessageHandler dependency of HttpClient
public abstract class FakeMessageHandler : HttpMessageHandler
{
protected sealed override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return DoSendAsync(request);
}
public abstract Task<HttpResponseMessage> DoSendAsync(HttpRequestMessage request);
}
In theory, I should be able to use the Matches() method in FakeItEasy to write a custom matching function. This would look something like this:
//NUnit Test
[TestFixture]
public class LoggingServiceTests
{
private LoggingService _loggingService;
private FakeMessageHandler _fakeMessageHandler;
private HttpClient _httpClient;
[SetUp]
public void SetUp()
{
_fakeMessageHandler = A.Fake<FakeMessageHandler>();
_httpClient = new HttpClient(_fakeMessageHandler);
_loggingService = new LoggingService(_httpClient);
}
[Test]
public async Task Logs_Error_Successfully()
{
var dateTime = new DateTime(2016, 11, 3);
var logEntry = new LogEntry
{
MessageText = "Fake Message",
DateLogged = dateTime
};
await _loggingService.Log(logEntry);
A.CallTo(() => _fakeMessageHandler.DoSendAsync(
A<HttpRequestMessage>.That.Matches(
m => DoesLogEntryMatch("Fake Message", dateTime, HttpMethod.Post,
"https://www.example.com/api/logging", m)))
).MustHaveHappenedOnceExactly();
}
private bool DoesLogEntryMatch(string expectedMessageText, DateTime expectedDateLogged,
HttpMethod expectedMethod, string expectedUrl, HttpRequestMessage actualMessage)
{
//TODO: still need to check expectedMessageText and expectedDateLogged from the HttpRequestMessage content
return actualMessage.Method == expectedMethod && actualMessage.RequestUri.ToString() == expectedUrl;
}
}
Checking the URL and the HttpMethod is easy enough (as demonstrated above). But, in order to check the payload, I need to check the content of the HttpRequestMessage. Here's where it gets tricky. The only way I've found to read the content of an HttpRequestMessage is to use one of the built-in async methods (i.e. ReadAsStringAsync, ReadAsByteArrayAsync, ReadAsStreamAsync, etc.) As far as I can tell, FakeItEasy does not support async/await operations inside of the Matches() predicate. Here's what I tried:
Convert DoesLogEntryMatch() method to async, and await the ReadAsStringAsync() call (DOES NOT WORK)
//Compiler error - Cannot convert async lambda expression to delegate type 'Func<HttpRequestMessage, bool>'.
//An async lambda expression may return void, Task or Task<T>,
//none of which are convertible to 'Func<HttpRequestMessage, bool>'
A.CallTo(() => _fakeMessageHandler.DoSendAsync(
A<HttpRequestMessage>.That.Matches(
async m => await DoesLogEntryMatch("Fake Message", dateTime, HttpMethod.Post,
"http://www.example.com/api/logging", m)))
).MustHaveHappenedOnceExactly();
private async Task<bool> DoesLogEntryMatch(string expectedMessageText, DateTime expectedDateLogged,
HttpMethod expectedMethod, string expectedUrl, HttpRequestMessage actualMessage)
{
var message = await actualMessage.Content.ReadAsStringAsync();
var logEntry = JsonConvert.DeserializeObject<LogEntry>(message);
return logEntry.MessageText == expectedMessageText &&
logEntry.DateLogged == expectedDateLogged &&
actualMessage.Method == expectedMethod && actualMessage.RequestUri.ToString() == expectedUrl;
}
Leave DoesLogEntryMatch as a non-async method, and don't await ReadAsStringAsync(). This seems to work when I tested it, but I have read that doing this could cause deadlocks in certain situations.
private bool DoesLogEntryMatch(string expectedMessageText, DateTime expectedDateLogged,
HttpMethod expectedMethod, string expectedUrl, HttpRequestMessage actualMessage)
{
var message = actualMessage.Content.ReadAsStringAsync().Result;
var logEntry = JsonConvert.DeserializeObject<LogEntry>(message);
return logEntry.MessageText == expectedMessageText &&
logEntry.DateLogged == expectedDateLogged &&
actualMessage.Method == expectedMethod && actualMessage.RequestUri.ToString() == expectedUrl;
}
Leave DoesLogEntryMatch as a non-async method, and await ReadAsStringAsync() inside of a Task.Run(). This spawns a new thread that will await the result, but allows the original method call to run synchronously. From what I've read, this is the only "safe" way to call an asynchronous method from a synchronous context (i.e. no deadlocks). This is what I wound up doing.
private bool DoesLogEntryMatch(string expectedMessageText, DateTime expectedDateLogged,
HttpMethod expectedMethod, string expectedUrl, HttpRequestMessage actualMessage)
{
var message = Task.Run(async () => await actualMessage.Content.ReadAsStringAsync()).Result;
var logEntry = JsonConvert.DeserializeObject<LogEntry>(message);
return logEntry.MessageText == expectedMessageText &&
logEntry.DateLogged == expectedDateLogged &&
actualMessage.Method == expectedMethod && actualMessage.RequestUri.ToString() == expectedUrl;
}
So, I got this working, but it seems like there should be a better way of doing this in FakeItEasy. Is there something equivalent to a MatchesAsync() method that would take a predicate that supports async/await?
There's no MatchesAsync in FakeItEasy; maybe it's something that could be added (though of course it could only work for async methods).
Leave DoesLogEntryMatch as a non-async method, and don't await ReadAsStringAsync(). This seems to work when I tested it, but I have read that doing this could cause deadlocks in certain situations.
In fact, I think that's the correct approach here. Using .Wait() or .Result is strongly discouraged in application code, but you're not in application code, you're in a unit test. The deadlock that can occur is caused by the presence of a SynchronizationContext, which exists in some frameworks (desktop frameworks like WPF or WinForms, classic ASP.NET), but not in the context of a unit test, so you should be fine. I used the same approach successfully in the past.

Using MockHttpClient nuget package in VS 2017

I am trying to test an adapter service in VS 2017 in C#. My test is failing because it is wanting a 400 through 499 response from the HTTPClient. When my test runs the service returns a 500.
So searching I found MockHttpClient nuget package but the examples given are not working when I try them in my test.
example:
https://github.com/codecutout/MockHttpClient/blob/master/README.md
I get an error saying
'MockHttpClient' is a namespace but is used like a type
I also added in a using MockHTTPClient at the top of my test.
What am I doing wrong?
getting error with the below
var mockHttpClient = new MockHttpClient();
mockHttpClient.When("the url I am using").Returns(HttpStatusCode.Forbidden)
It's a name clash with the namespace. The class and namespace share the same name.
Remove the using statement and use this instead:
var mockHttpClient = new MockHttpClient.MockHttpClient();
Poor choice of names for this library and a horrific amount of dependencies. I would stay away if I were you.
UPDATE:
You asked for an alternative so here is what I recently did for a project:
The HttpClient class has a constructor that takes an HttpMessageHandler object, so you can pass your own handler and simulate the behavior.
Create a class that derives from DelegatingHandler and overrides the send behavior:
public class TestHandler : DelegatingHandler
{
private Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handler;
public TestHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler)
{
_handler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handler(request, cancellationToken);
}
public static Task<HttpResponseMessage> OK()
{
return Task.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.OK));
}
public static Task<HttpResponseMessage> BadRequest()
{
return Task.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.BadRequest));
}
}
Then on your test, you use your handler in the constructor:
//Create an instance of the test handler that returns a bad request response
var testHandler = new TestHandler((r, c) =>
{
return TestHandler.BadRequest();
});
//Create the HTTP client
var client = new HttpClient(testHandler);
//Fake call, will never reach out to foo.com
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.foo.com");
request.Content = new StringContent("test");
//This will call the test handler and return a bad request response
var response = client.SendAsync(request).Result;
Notice I have a couple of convenience static methods in there to create the handling functions for me.

How to mock HttpClient request in C#

I have some code like this and I tried using HttpMock and MockHttp but it was of no use is there any otherway to mock the HttpClient Request? I can't edit my code and implement an interface to write UnitTestCases as this would involve modifying lot of other code!
public GetData(string url,string data)
{
//does some logic and adds the parameter to URL
string finalurl=url+"?data=data";
using (var client = new HttpClient(new TokenHandler()))
{
var response = await client.GetAsync(finalurl);
var responsedata = await
response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.OK)
{
responsefinal = responsedata;
}
}
return responsefinal;
}
If you want to avoid running another server altogether, you can use the overloads of the HttpClient constructor. This would require some minor changes to your code (but no need for any interface over your client).
The HttpClient constructor takes a an instance of HttpMessageHandler. You can create a mock implementation of this that will enable you to inspect the request you get from the client and return an HttpResponseMessage, allowing you to mock a variety of scenarios:
public class MockMessageHandler: HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
// Capture request properties, return response
}
}
You can then pass in a factory method (a function delegate if necessary) to generate instances of your message handler as required:
public GetData(String url, String data, Func<HttpMessageHandler> handlerFactory)
And then use it in the HttpClientConstructor:
using (var client = new HttpClient(handlerFactory()))

Moq: How to test a class using Nunit with an internal HttpClient?

I run my tests inside nUnit and normally I can mock out dependencies and have then Return certain values or throw errors.
I have a class that as an internal HttpClient and I would like to test the class, what are my options.
here is my code, its not complete so as not to flood the message. As you can see I am using the HttpClient internally and not injected as a dependency. The class throws a number of custom exceptions, I would like to Moq these otherwise I need to pass REAL username and passwords that would give me the status codes i required to throw the exceptions.
Anyone have an ideas? If I can't mock the httpclient then i can never test my class that it raises exceptions.
Do I really have to change HttpClient to a dependency on the constructor ?
public bool ItemsExist(string itemValue)
{
var relativeUri = string.Format(UrlFormatString, itemValue.ToUpper());
var uri = new Uri(new Uri(this.baseUrl), relativeUri);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", this.encodedCredentials);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(uri).Result;
switch (response.StatusCode)
{
case HttpStatusCode.Unauthorized:
// DO something here
throw new CustomAuthorizationException();
case HttpStatusCode.Forbidden:
throw new CustomAuthenticationException();
}
return true;
Let me suggest a bit easier solution, without a need to abstract/wrap httpclient, that i believe works perfectly with mocking frameworks.
You need to create a class for fake HttpMessageHandler, like here:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException("Rember to setup this method with your mocking framework");
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
Such created HttpMessageHandler can be used when instantiating HttpClient:
var msgHandler = new Mock<FakeHttpMessageHandler>() { CallBase = true };
var fakeclient = new HttpClient(msgHandler.Object);
And you can setup methods (here using Moq):
msgHandler.Setup(t => t.Send(It.Is<HttpRequestMessage>(
msg =>
msg.Method == HttpMethod.Post &&
msg.RequestUri.ToString() == "http://test.te/item/123")))
.Returns(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
You can now user fakeclient instead when necessary.
You can't unit test it like that. It's like you mentioned: HttpClient is a dependency, and as such, it should be injected.
Personally, I would create my own IHttpClient interface, implemented by HttpClientWrapper, which wraps around the System.Net.HttpClient. IHttpClient would then be passed as a dependency to your object's contructor.
As follows, HttpClientWrapper can't be unit tested. I would, however, write a couple of integration tests to make sure the wrapper is well written.
Edit:
IHttpClient doesn't have to be a "valid" interface for HttpClient. It only has to be an interface that suits your needs. It can have as many or as few methods as you want.
Picture this: HttpClient allows you to do many things. But in your project, you're only calling the GetAsync(uri).Result method, nothing else.
Given this scenario, you would write the following interface and implementation:
interface IHttpClient
{
HttpResponseMessage Get(string uri);
}
class HttpClientWrapper : IHttpClient
{
private readonly HttpClient _client;
public HttpClientWrapper(HttpClient client)
{
_client = client;
}
public HttpResponseMessage Get(string uri)
{
return _client.GetAsync(new Uri(uri)).Result;
}
}
So, as I stated previously, the interface only has to suit your needs. You don't have to wrap around the WHOLE HttpClient class.
Obviously, you would then moq your object like this:
var clientMock = new Mock<IHttpClient>();
//setup mock
var myobj = new MyClass(clientMock.object);
And to create an actual object:
var client = new HttpClientWrapper(new HttpClient());
var myobj = new MyClass(client );
Edit2
OH! And don't forget that IHttpClient should also extend the IDisposable interface, very important!
Another option is to use Flurl [disclosure: I'm the author], a library for building and calling URLs. It includes testing helpers that make faking all HTTP incredibly easy. No need for wrapper interfaces.
For starters, your HTTP code itself would look something like this:
using Flurl;
using Flurl.Http;
...
try {
var response = this.baseUrl
.AppendPathSegment(relativeUri)
.WithBasicAuth(username, password)
.WithHeader("Accept", "application/json")
.GetAsync().Result;
return true;
}
catch (FlurlHttpException ex) {
// Flurl throws on unsuccessful responses. Null-check ex.Response,
// then do your switch on ex.Response.StatusCode.
}
Now for the testing fun:
using Flurl.Http.Testing;
...
[Test]
public void ItemsExists_SuccessResponse() {
// kick Flurl into test mode - all HTTP calls will be faked and recorded
using (var httpTest = new HttpTest()) {
// arrange
test.RespondWith(200, "{status:'ok'}");
// act
sut.ItemExists("blah");
// assert
test.ShouldHaveCalled("http://your-url/*");
}
}
Get it on NuGet:
PM> Install-Package Flurl.Http

How to mock(rhino.mocks) response object from base.SendAsync() in MVC4.5 Web API handler SendAsync() method

I have to write unit test on ASP.NET MVC Web API Controller with Rhino.Mock
I have a handler named AHandler.cs with inherts from System.Net.Http.HttpClientHandler class.
The singnature SendAsync method of AHandler.cs is like followings :
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
var response = base.SendAsync(request, cancellationToken).Result;
if (response.IsSuccessStatusCode)
{
.....
}
}
the base keyword above means HttpClientHandler and its SendAsync() method is "protected"!!!
Now I try to mock the object "base.SendAsync(request, cancellationToken).Result" and got the hand-made response result I wanted.
But it seems that Rhino mocks can't see the "base" keyword when I wrote the followings code :
var mockbase = MockRepository.GenerateMock<AHandler>;
mockbase.Stub(x => x.base <=== can't see base keyword
^^^^^
So I change another way and try to mock the HttpClientHandler class
var mockbase = MockRepository.GenerateMockHttpClientHandler>;
mockbase.Stub(x => x. <== I can't see SendAsync() method, becase it is protected !!
Now I really suffer in it !!
Can anybody give me some advice that how to made a custom response in MVC handler ?!
very thanks !!
Why do you want to mock a handler in first place ? You can inject an specific dummy implementation for your tests. That handler will return a new HttpResponse message expected by your tests.
public class MyDummyHttpHandler : DelegatingHandler
{
HttpResponseMessage response;
public MyDummyHttpHandler(HttpResponseMessage response)
{
this.response = response;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
TaskCompletionSource<HttpResponseMessage> tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(this.response);
return tsc.Task;
}
}

Categories

Resources