I am new to NSubstitute. The test fails because the second arrangement causes invocation of the first one and makes it fail even before the "act". I am not sure if i should make multiple arrangements on the same method. But I feel like It shouldn't be calling the first arrangement regardless, since the parameters don't match.
public interface IMediator
{
Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default(CancellationToken));
Task Send(IRequest request, CancellationToken cancellationToken = default(CancellationToken));
Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default(CancellationToken))
where TNotification : INotification;
}
public class MyMessage : IRequest<MyResponse> {}
public class MyResponse {}
public class MyMessage2 : IRequest<MyResponse> {}
[Fact]
public async Task Mock_Fail() {
var mediatr = Substitute.For<IMediator>();
var myMessage = new MyMessage();
var myMessage2 = new MyMessage();
var myResponse = new MyResponse();
var myResponse2 = new MyResponse();
mediatr.Send(Arg.Any<MyMessage>())
.Returns((ci) => {
Assert.Same(myMessage, ci[0]); //That fails
return myResponse;
});
mediatr.Send(Arg.Any<MyMessage2>())
.Returns((ci) => {
return myResponse2;
});
//Execution never reaches here
var response = await mediatr.Send(myMessage);
var response2 = await mediatr.Send(myMessage2);
}
Normally I'd test this more like:
mediatr.Send(myMessage)
.Returns(ci => myResponse);
mediatr.Send(myMessage2)
.Returns(ci => myResponse2);
There are a few approaches to overriding previous stubs that throw, but I think the best approach is to avoid the problem where possible. :)
EDIT after more info provided:
This looks like a bug in NSubstitute's Arg.Any handling. A work around is to use Arg.Is<MyMessage>(x => x!=null) as shown in that issue description. Overall I would focus on stubbing more specifically to avoid the calls overlapping as per my original answer.
Related
Injecting state into your HttpRequest when using IHttpClientFactory is achievable by populating HttpRequestMessage.Properties see Using DelegatingHandler with custom data on HttpClient
Now if I have third party extensions on HttpClient (such as IdentityModel), how would I intercept these http requests using custom state?
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = factory.CreateClient();
// GetDiscoveryDocumentAsync is a third party extension method on HttpClient
// I therefore cannot inject or alter the request message to be handled by the InterceptorHandler
var discovery = await httpClient.GetDiscoveryDocumentAsync();
// I want id to be associated with any request / response GetDiscoveryDocumentAsync is making
}
The only plausible solution I currently have is to override HttpClient.
public class InspectorHttpClient: HttpClient
{
private readonly HttpClient _internal;
private readonly int _id;
public const string Key = "insepctor";
public InspectorHttpClient(HttpClient #internal, int id)
{
_internal = #internal;
_id = id;
}
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// attach data into HttpRequestMessage for the delegate handler
request.Properties.Add(Key, _id);
return _internal.SendAsync(request, cancellationToken);
}
// override all methods forwarding to _internal
}
A then I'm able to intercept these requests.
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = new InspectorHttpClient(factory.CreateClient(), id);
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
Is that a plausible solution? Something tell me now not to override HttpClient. Quoting from https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
The HttpClient also acts as a base class for more specific HTTP clients. An example would be a FacebookHttpClient providing additional methods specific to a Facebook web service (a GetFriends method, for instance). Derived classes should not override the virtual methods on the class. Instead, use a constructor overload that accepts HttpMessageHandler to configure any pre- or post-request processing instead.
I almost included this in my other answer as an alternative solution, but I figured it was too long already. :)
The technique is practically the same, but instead of HttpRequestMessage.Properties, use AsyncLocal<T>. "Async local" is kind of like thread-local storage but for a specific asynchronous code block.
There are a few caveats to using AsyncLocal<T> that aren't particularly well-documented:
Use an immutable nullable type for T.
When setting the async local value, return an IDisposable that resets it.
If you don't do this, then only set the async local value from an async method.
You don't have to follow these guidelines, but they will make your life much easier.
With that out of the way, the solution is similar to the last one, except it just uses AsyncLocal<T> instead. Starting with the helper methods:
public static class AmbientContext
{
public static IDisposable SetId(int id)
{
var oldValue = AmbientId.Value;
AmbientId.Value = id;
// The following line uses Nito.Disposables; feel free to write your own.
return Disposable.Create(() => AmbientId.Value = oldValue);
}
public static int? TryGetId() => AmbientId.Value;
private static readonly AsyncLocal<int?> AmbientId = new AsyncLocal<int?>();
}
Then the calling code is updated to set the ambient value:
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
using (AmbientContext.SetId(id))
{
var httpClient = factory.CreateClient();
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
}
Note that there is an explicit scope for that ambient id value. Any code within that scope can get the id by calling AmbientContext.TryGetId. Using this pattern ensures that this is true for any code: synchronous, async, ConfigureAwait(false), whatever - all code within that scope can get the id value. Including your custom handler:
public class HttpClientInterceptor : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var id = AmbientContext.TryGetId();
if (id == null)
throw new InvalidOperationException("The caller must set an ambient id.");
// associate the id with this request
Database.InsertEnquiry(id.Value, request);
return await base.SendAsync(request, cancellationToken);
}
}
Followup readings:
Blog post on "async local" - written before AsyncLocal<T> existed, but has details on how it works. This answers the questions "why should T be immutable?" and "if I don't use IDisposable, why do I have to set the value from an async method?".
First of all
var myString = await actionContext.Request.Content.ReadAsStringAsync();
should always work. But it doesn't. Here we go:
AirplaneController.cs
[ValidateAirplane]
[HydrateAirplane]
public class AirplaneController : ApiController
ValidateAirplane.cs (see the amazing magic line)
public class ValidateAirplaneAttribute : FilterAttribute, IAuthorizationFilterpublic class ValidateAirplaneAttribute : FilterAttribute, IAuthorizationFilter
...
public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var json = await actionContext.Request.Content.ReadAsStringAsync(); // <-- amazing magic line
return await continuation();
}
HydrateAirplane.cs
public class HydrateAirplaneAttribute : FilterAttribute, IActionFilter
...
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var json = await actionContext.Request.Content.ReadAsStringAsync();
return await continuation();
}
If I comment out the amazing magic line then json in ExecuteAuthorizationFilterAsync comes back as an empty string. However if I don't comment out the amazing magic line I get the json (the airplane JSON that came from the client).
At first I thought this might be a timing issue so I tried doing a await Task.Delay(5000) in HydrateAirplane.cs just before trying to read the json but it has no effect.
Any idea what the problem is?
This is more of a work around than an answer. If you have a better answer please share. I noticed there's a ReadAsStreamAsync and I was able to get that to work. Maybe this is a bug in WebAPI? I couldn't find the code on github so I don't know for sure. My work around is to use this extension method instead of ReadAsStringAsync:
public async static Task<string> GetRequestBody(this HttpActionContext actionContext)
{
var bodyStream = await actionContext.Request.Content.ReadAsStreamAsync();
bodyStream.Position = 0;
var bytes = new byte[bodyStream.Length];
await bodyStream.ReadAsync(bytes, 0, bytes.Length);
bodyStream.Position = 0; // <-- i don't know why but this line is important. it doesn't work on subsequent calls without this. ...to be clear, it absolutely should work. i consider this to be a WebAPI work around. that is, i should not have to ever even think about this.
return Encoding.ASCII.GetString(bytes);
}
The reason this isn't wrapped in a using is it was wrapped in a using but then WebAPI started throwing a stream is not readable error which I guess means they use the exact same stream that they return to you internally.
I would like to unit test a class that uses HttpClient. We injected the HttpClient object in the class constructor.
public class ClassA : IClassA
{
private readonly HttpClient _httpClient;
public ClassA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SendRequest(SomeObject someObject)
{
//Do some stuff
var request = new HttpRequestMessage(HttpMethod.Post, "http://some-domain.in");
//Build the request
var response = await _httpClient.SendAsync(request);
return response;
}
}
Now we would like to unit test the ClassA.SendRequest method. We are using Ms Test for unit testing framework and Moq for mocking.
When we tried to mock the HttpClient, it throws NotSupportedException.
[TestMethod]
public async Task SendRequestAsync_Test()
{
var mockHttpClient = new Mock<HttpClient>();
mockHttpClient.Setup(
m => m.SendAsync(It.IsAny<HttpRequestMessage>()))
.Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
}
How can we solve this issue?
That particular overload method is not virtual so is unable to be overridden by Moq.
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Which is why it throws NotSupportedException
The virtual method you are looking for is this method
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
However mocking HttpClient is not as simple as it seems with its internal message handler.
I suggest using a concrete client with a custom message handler stub that will allow for more flexibility when faking the request.
Here is an example of a delegating handler stub.
public class DelegatingHandlerStub : DelegatingHandler {
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub() {
_handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
}
public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return _handlerFunc(request, cancellationToken);
}
}
Note the default constructor is doing basically what you were trying to mock before. It also allows for more custom scenarios with a delegate for the request.
With the stub, the test can be refactored to something like
public async Task _SendRequestAsync_Test() {
//Arrange
var handlerStub = new DelegatingHandlerStub();
var client = new HttpClient(handlerStub);
var sut = new ClassA(client);
var obj = new SomeObject() {
//Populate
};
//Act
var response = await sut.SendRequest(obj);
//Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
}
Moq can mock out protected methods, such as SendAsync on the HttpMessageHandler that you can provide to HttpClient in its constructor.
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var client = new HttpClient(mockHttpMessageHandler.Object);
Copied from https://www.thecodebuzz.com/unit-test-mock-httpclientfactory-moq-net-core/
Propper mocking with HttpClient is hard work as it was written before most people did unit testing in dotnet. Sometimes I setup a stub HTTP server that returns canned responses based on pattern matching the request url, meaning you test real HTTP requests not mocks but to a localhost server. Using WireMock.net makes this really easy and runs fast enough to satisfy most of my unit testing needs.
So instead of http://some-domain.in use a localhost server setup on some port, and then:
var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
Request.Create()
.WithPath("/").UsingPost()
)
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody("{'attr':'value'}")
);
You can find a more details and guidance on using wiremock in tests here.
I recently had to mock HttpClient, and I used Moq.Contrib.HttpClient. It was what I needed, and simple to use, so I thought I'd throw it out there.
Here is an example of general usage:
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
// A simple example that returns 404 for any request
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");
// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
.ReturnsResponse(bytes, configure: response =>
{
response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
})
.Verifiable(); // Naturally we can use Moq methods as well
// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
For more info, check out author's blog post here.
All calls to the service should work through personal channel. So all methods which have access to the server proxy should look like this one:
public async Task<SDRLocation[]> FindLocationsAsync(string searchString)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
SomeProxy = channel.CreateChannel();
Location[] locationEntitiesFound = await SomeProxy.FindLocationsAsync(searchString);
((IChannel)SomeProxy ).Close();
return locationEntitiesFound.Select(x => new SDRLocation(x)).ToArray();
}
But because I have a lot of methods like this service calls I tried to avoid code duplication and create this method wrapper:
public TResult HandleServiceCall<TResult>(Func<IPlantOrgQueryService, TResult> serviceMethod)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
IQueryService newProxy = channel.CreateChannel();
TResult results = serviceMethod(newProxy);
((IChannel)newProxy).Close();
return results;
}
now I expect to make everywhere calls like this :
public async Task<SDRLocation[]> FindLocationsAsync(string searchString)
{
Location[] locationEntitiesFound = await HandleServiceCall(x => x.FindLocationsAsync(searchString));
return locationEntitiesFound.Select(x => new SDRLocation(x)).ToArray();
}
But I end up with error "The communication object, System.ServiceModel.Channels.ClientReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted."
Not understand what is wrong because method without HandleServiceCall works just fine...
Help please
The type of TResult will let you know what's wrong. It's Task<Location[]>. So you're disposing the proxy (via Close) before the asynchronous call is complete.
The fix is to await the Task before calling Close, just like your original code is doing. This should do the trick:
public async Task<TResult> HandleServiceCall<TResult>(Func<IPlantOrgQueryService, Task<TResult>> serviceMethod)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
IQueryService newProxy = channel.CreateChannel();
TResult results = await serviceMethod(newProxy);
((IChannel)newProxy).Close();
return results;
}
I'm converting some async/await code to chained tasks, so I can use it in the released framework. The await code looks like this
public async Task<TraumMessage> Get() {
var message = await Invoke("GET");
var memorized = await message.Memorize();
return memorized;
}
where
Task<TraumMessage> Invoke(string verb) {}
Task<TraumMessage> Memorize() {}
I was hoping to chain Invoke and Memorize to return the task produced by Memorize, but that results in a Task<Task<TraumMessage>. The solution i've ended up is a TaskCompletionSource<TraumMessage> as my signal:
public Task<TraumMessage> Get() {
var completion = new TaskCompletionSource<TraumMessage>();
Invoke("GET").ContinueWith( t1 => {
if(t1.IsFaulted) {
completion.SetException(t1.Exception);
return;
}
t1.Result.Memorize().ContinueWith( t2 => {
if(t2.IsFaulted) {
completion.SetException(t2.Exception);
return;
}
completion.SetResult(t2.Result);
});
});
return completion.Task;
}
Is there a way to accomplish this without the TaskCompletionSource?
Yes, the framework comes with a handy Unwrap() extension method for exactly what you want.
Invoke("GET").ContinueWith( t => t.Result.Memorize() ).Unwrap();
If you're doing cancellation then you'll need to pass cancel tokens into the appropriate places, obviously.
I think that's pretty much the only way to accomplish what you want. Chaining disparate Tasks together isn't supported by the continuation APIs, so you have to resort to using a TaskCompletionSource like you have to coordinate the work.
I don't have the Async CTP installed on this machine, but why don't you take a look at the code with a decompiler (or ILDASM if you know how to read IL) to see what it's doing. I bet it does something very similar to your TCS code under the covers.
You can use attached child tasks. The parent task will only transition into the completed status when all child tasks are complete. Exceptions are propagated to the parent task.
You will need a result holder, as the result will be assigned after the parent task's delegate has finished, but will be set when the parent tasks continuations are run.
Like this:
public class Holder<T> where T: class
{
public T Value { get; set; }
}
public Task<Holder<TraumMessage>> Get() {
var invokeTask = Invoke("GET");
var result = invokeTask.ContinueWith<Holder<TraumMessage>>(t1 => {
var holder = new Holder<TraumMessage>();
var memorizeTask = t1.Result.Memorize();
memorizeTask.ContinueWith(t2 => {
holder.Value = t2.Result;
}, TaskContinuationOptions.AttachedToParent);
return holder;
});
return result;
}