NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

Exception thrown when mocking IRequestCookieCollection

Open koliva8245 opened this issue 4 years ago • 4 comments

This is unusual and I have no idea what's going on.

When attempting to mock TryGetValue in httpContext.Request.Cookies:

[TestMethod]
public void Test()
{
    var httpContext = new DefaultHttpContext();

    httpContext.Request.Cookies = Substitute.For<IRequestCookieCollection>();
    httpContext.Request.Cookies.TryGetValue("hello", out Arg.Any<string?>()).Returns(x =>
    {
        x[1] = "world!";
        return true;
    });
}

The code will give the following exception:

NSubstitute.Exceptions.UnexpectedArgumentMatcherException: 'Argument matchers (Arg.Is, Arg.Any) should only be used in place of member arguments. Do not use in a Returns() statement or anywhere else outside of a member call.
Correct use:
  sub.MyMethod(Arg.Any<string>()).Returns("hi")
Incorrect use:
  sub.MyMethod("hi").Returns(Arg.Any<string>())'

Any idea if I'm doing something wrong or is this possibly a bug?

Environment:

  • NSubstitute version: 4.2.2
  • NSubstitute.Analyzers version: 1.0.14
  • Platform: netcoreapp3.1 (asp.net core) on Windows

koliva8245 avatar Feb 19 '21 00:02 koliva8245

Hi @koliva8245 ,

Thanks for the good repro steps! 👍

This does seem odd. This works as expected:

            var cookies = Substitute.For<IRequestCookieCollection>();
            cookies.TryGetValue("hello", out Arg.Any<string>()).Returns(x => {
                x[1] = "world!";
                return true;
            });

This test fails:

            var httpContext = new DefaultHttpContext();
            var c  = Substitute.For<IRequestCookieCollection>();
            httpContext.Request.Cookies = c;
            Assert.Same(c, httpContext.Request.Cookies);
/* FAIL:
Assert.Same() Failure
Expected: ObjectProxy []
Actual:   RequestCookieCollection []
*/

So the assignment to Cookies does not seem to be working? 🤯

dtchepak avatar Feb 19 '21 03:02 dtchepak

var httpContext = new DefaultHttpContext();
var cookies = Substitute.For<IRequestCookieCollection>();
cookies.TryGetValue("hello", out Arg.Any<string?>()).Returns(x =>
{
    x[1] = "world!";
    return true;
});

var result = cookies.TryGetValue("hello", out var value);
Assert.AreEqual("world!", value); // passes

httpContext.Request.Cookies = cookies;
httpContext.Request.Cookies.TryGetValue("hello", out var httpValue);

Assert.AreEqual("world!", httpValue); // fails

Yeah, the assignment to httpContext.Request.Cookies isn't working as it should. So it looks like I can't write test code for methods that use cookies. 😢

Any workarounds?

koliva8245 avatar Feb 19 '21 16:02 koliva8245

Is it possible to use the real cookie collection itself? That would be ideal for this case I think.

Otherwise you could look for mockable Http abstractions or check the source (here?) as to why the assignment is acting strangely.

dtchepak avatar Feb 21 '21 23:02 dtchepak

Unfortunately the only implementation of IRequestCookieCollection is RequestCookieCollection which has been set as internal.

It seems that the httpContext.Request.Cookies value cannot be changed, since even setting is as null doesn't even work.

So I said heck with it and just mocked HttpContext instead.

var httpContext = Substitute.For<HttpContext>();
httpContext.Request.Cookies.TryGetValue("hello", out Arg.Any<string?>()).Returns(x =>
{
    x[1] = "world!";
    return true;
});
httpContext.Request.Cookies.TryGetValue("hello", out string? value);

Assert.AreEqual("world!", value);

koliva8245 avatar Feb 22 '21 01:02 koliva8245

I assume your question has been answered, if not, please let us know!

304NotModified avatar Apr 29 '24 12:04 304NotModified