10

I have just upgraded from .NET Core 2.2 to 3.1. I have tests to confirm that extension methods I've added to HttpContext.Request are working. I was previously able to do things like:

    var context = new DefaultHttpContext();
    var c = new Dictionary<string, string> {{"test", "passed"}};
    context.Request.Cookies = new RequestCookieCollection(cookies);

    var result = context.Request.GetPrimedValue();

Is this impossible now? I tried using Moq for this, but there are far too many things blocking me from being able to set the Cookies property with anything usable, it seems. What is the resolution for this?

note: I understand that this was using an internal class which shouldn't have been internal, so I don't disagree with the internal namespace being hidden, but I'm not sure what my alternatives are.

Dinerdo
  • 410
  • 4
  • 25
  • Then mock `IRequestCookieCollection` Interface and setup the expected behavior – Nkosi Feb 28 '20 at 10:17
  • Please read the entire post. That would seemingly require an absurd amount of code to set up everything because I use this in the authorization process, and there seem to be a whole lot of checks just to make sure that the cookie collection you're creating can even be used to set the cookies property. When I use a mock for this and try to set the cookies property, there is a lot of validation which fails so the cookies property is never set. – Dinerdo Feb 28 '20 at 14:27
  • @Dinerdo, did you happen to resolve this issue? I've been looking high and low for a solution as well. – Frank Thomas Mar 10 '20 at 20:46
  • Unfortunately, I am ignoring these tests for now. :( – Dinerdo Mar 10 '20 at 23:38
  • 1
    I'm hitting the exact same restriction here. That thing is not mockable at all now. The time it requires to mock everything, is probably not well spent if everything around it tested... That's a real bummer. – Marc-Andre R. Apr 24 '20 at 18:40
  • I can create a RequestCookieCollection() by adding "using Microsoft.AspNetCore.Http.Internal;" statement. What's the question about? – brainoverflow98 Sep 15 '20 at 20:29

4 Answers4

10

By manipulating some foundation classes, I am able to produce a mock cookie collection. However it is more like a workaround and might not work in future releases. You might want to give it a try and see how long it can go ...

With the helper function:

    private static IRequestCookieCollection MockRequestCookieCollection(string key, string value)
    {
            var requestFeature = new HttpRequestFeature();
            var featureCollection = new FeatureCollection();

            requestFeature.Headers = new HeaderDictionary();
            requestFeature.Headers.Add(HeaderNames.Cookie, new StringValues(key + "=" + value));

            featureCollection.Set<IHttpRequestFeature>(requestFeature);

            var cookiesFeature = new RequestCookiesFeature(featureCollection);

            return cookiesFeature.Cookies;
    }

Now your unit test code shall become

    var context = new DefaultHttpContext();

    context.Request.Cookies = MockRequestCookieCollection("test", "passed");
victor6510
  • 156
  • 1
  • 4
2

Sorry I can't add this as a comment, but if you create your own RequestCookieCollection class, based on the original one in the .net core codebase:

https://github.com/dotnet/aspnetcore/blob/4ef204e13b88c0734e0e94a1cc4c0ef05f40849e/src/Http/Http/src/Internal/RequestCookieCollection.cs

Then you could use this new class to create your cookie collection in your unit tests project. I tried this approach and it works.

Matt B
  • 116
  • 9
1

@Victor6510's answer worked great for me until i needed to create a fake cookie that contained JSON. then i ran into this bug: https://github.com/dotnet/aspnetcore/issues/29304 so i had to come up with additional way.

Using NSubstitute:

[TestClass]
public class ImpersonationMiddlewareTest
{
    private HttpContext _sHttpContext;
    private HttpRequest _sHttpRequest;
    private IRequestCookieCollection _sCookieCollection;

    [TestInitialize]
    public void Initialize()
    {
        _sHttpContext = Substitute.For<HttpContext>();
        _sHttpRequest = Substitute.For<HttpRequest>();
        _sCookieCollection = Substitute.For<IRequestCookieCollection>();
    }

    [TestMethod]
    public async Task Invoke_GoodCookie_CreatesImpersonationIdentity()
    {
        //arrange
        var cookieList = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string,string>("cookiename", "cookievalue")
        };
        _sCookieCollection.GetEnumerator().Returns(e => cookieList.GetEnumerator());
        _sCookieCollection.ContainsKey("cookiename").Returns(true);

        _sHttpRequest.Cookies.Returns(_sCookieCollection);

        _sHttpContext.Request.Returns(_sHttpRequest);

        //act
        myMiddleware.InvokeAsync(_sHttpContext);

        //assert
    }
}
josh
  • 1,039
  • 1
  • 11
  • 22
0

Another way of mocking cookies by appending request headers.

[TestMethod]
public void Test()
{
    // Arrange
    var controller = new Controller();
    var cookie = new StringValues(COOKIE_NAME + "=" + COOKIE_VALUE);
    controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() };
    controller.ControllerContext.HttpContext.Request.Headers.Add(HeaderNames.Cookie, cookie);

    // Act
    var result = Sut.Action();

    // Assert
    // TODO
}
SoniCue
  • 152
  • 5