HristoKolev HristoKolev - 10 months ago 90
C# Question

Argument matching not working properly with NSubstitute

I have this code and I can't get httpClient to return the string response no matter what.

HttpClient httpClient = Substitute.For<HttpClient>();

httpClient.PostAsync("/login", Arg.Any<StringContent>())
.Returns(this.StringResponse("{\"token\": \"token_content\"}"));

Console.WriteLine(await httpClient.PostAsync("/login", new StringContent(string.Empty)) == null); // True

Here is the
method if someone wants to reproduce this:

private HttpResponseMessage StringResponse(string content)
var response = new HttpResponseMessage(HttpStatusCode.OK);

if (content != null)
response.Content = new StringContent(content);

return response;

What am I doing wrong?

It works when I do

httpClient.PostAsync("/login", Arg.Any<StringContent>())
.ReturnsForAnyArgs(this.StringResponse("{\"token\": \"token_content\"}"));

But I don't need just any arguments, I need one of them to be the string
and the other to be of type

I tried to put something more general in the
call like
but that doesn't work too.

I'm using
NSubstitute 2.0.0-rc
on .NET Core, but I also tried using
NSubstitute 1.10.0
on standart console application and got the same result.

Answer Source

The problem here is that the public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content) method isn't virtual and NSubstitute cannot correctly link your argument specification and return value to the method. That's a problem of all mocking libraries based on Castle.Core.

What's important is that NSubstitute links the specification to the first virtual method in the execution stack which turns out to be public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) method somewhere down the stack. You were "lucky" to have the same return type in it. And as you can see it has different arguments that obviously don't match with those you provided. Surprisingly ReturnsForAnyArgs() works because it doesn't check the argument specification you provided.