简体   繁体   English

在 .NET Core 3.1 中,RequestCookieCollection 不能再用于在单元测试中创建 cookies

[英]In .NET Core 3.1, the RequestCookieCollection can no longer be used to create cookies in unit tests

I have just upgraded from .NET Core 2.2 to 3.1.我刚刚从 .NET Core 2.2 升级到 3.1。 I have tests to confirm that extension methods I've added to HttpContext.Request are working.我进行了测试以确认我添加到HttpContext.Request的扩展方法是否正常工作。 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.我尝试为此使用 Moq,但似乎有太多东西阻止我使用任何可用的东西设置 Cookies 属性。 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.注意:我知道这是使用内部 class,它不应该是内部的,所以我不反对隐藏内部命名空间,但我不确定我的替代方案是什么。

By manipulating some foundation classes, I am able to produce a mock cookie collection.通过操作一些基础类,我能够生成一个模拟 cookie 集合。 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");

Another way of mocking cookies by appending request headers.另一种通过附加请求标头来模拟 cookie 的方法。

[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
}

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:抱歉,我无法将其添加为评论,但是如果您基于 .net 核心代码库中的原始类创建自己的 RequestCookieCollection 类:

https://github.com/dotnet/aspnetcore/blob/4ef204e13b88c0734e0e94a1cc4c0ef05f40849e/src/Http/Http/src/Internal/RequestCookieCollection.cs 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.然后您可以使用这个新类在您的单元测试项目中创建您的 cookie 集合。 I tried this approach and it works.我尝试了这种方法并且它有效。

@Victor6510's answer worked great for me until i needed to create a fake cookie that contained JSON. @Victor6510 的回答对我很有用,直到我需要创建一个包含 JSON 的假 cookie。 then i ran into this bug: https://github.com/dotnet/aspnetcore/issues/29304 so i had to come up with additional way.然后我遇到了这个错误: https : //github.com/dotnet/aspnetcore/issues/29304所以我不得不想出其他方法。

Using NSubstitute:使用 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
    }
}

I found that the simplest solution for my purposes was to leverage the fact that a Dictionary<string, string> is almost a complete implementation of IRequestCookieCollection .我发现最简单的解决方案是利用Dictionary<string, string>几乎是IRequestCookieCollection的完整实现这一事实。 Just need to expose the Keys property as an ICollection<string> :只需要将Keys属性公开为ICollection<string>

public class RequestCookieCollection : Dictionary<string, string>, IRequestCookieCollection
{
    public new ICollection<string> Keys => base.Keys;
}

I know the question author resisted the idea of mocking IRequestCookieCollection , but ReSharper makes it easy to build a mock which can be adapted as needed.我知道问题作者反对 mocking IRequestCookieCollection的想法,但 ReSharper 可以轻松构建可根据需要进行调整的模拟。 Here's what I did.这就是我所做的。

  1. Create a mock class of IRequestCookieCollection.创建 IRequestCookieCollection 的模拟 class。

     private class MockRequestCookieCollection: IRequestCookieCollection { }
  2. Allow ReSharper to implement the missing members.允许 ReSharper 实现缺失的成员。

     private class MockRequestCookieCollection: IRequestCookieCollection { public string? this[string key] => throw new NotImplementedException(); public int Count => throw new NotImplementedException(); public ICollection<string> Keys => throw new NotImplementedException(); public bool ContainsKey(string key) { throw new NotImplementedException(); } public IEnumerator<KeyValuePair<string, string>> GetEnumerator() { throw new NotImplementedException(); } public bool TryGetValue(string key, out string? value) { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } }
  3. Build out the members you need.建立你需要的成员。 For my purposes, I needed to add a field and constructor and complete the indexer.出于我的目的,我需要添加一个字段和构造函数并完成索引器。

     private readonly IDictionary<string, string> _cookies; public MockRequestCookieCollection(IDictionary<string, string> cookies) { _cookies = cookies; } public string? this[string key] { get { try { return _cookies[key]; } catch { return null; } } }
  4. Use the mock in tests.在测试中使用模拟。

     var dictionary = new Dictionary<string, string> { { "foo", "1" }, { "bar", "2" } }; var cookies = new MockRequestCookieCollection(dictionary);

Disclaimer: I used Visual Studio 2022 and .NET 6, but the concepts should work in .NET Core 3.1.免责声明:我使用的是 Visual Studio 2022 和 .NET 6,但这些概念应该适用于 .NET Core 3.1。

A very simple solution is to use ImpromptuInterface , a duck casting framework.一个非常简单的解决方案是使用ImpromptuInterface ,一个鸭子铸造框架。

// using ImpromptuInterface;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 .net core 3.1 中 RequestCookieCollection 和 ResponseCookies 的替代方案是什么? - what's the alternative of RequestCookieCollection and ResponseCookies in .net core 3.1? .net core 3.1 身份:cookie 创建“请求太长”错误 - .net core 3.1 identity: cookies create a "request too long" error .net core 3.1 无法删除 cookie - .net core 3.1 cannot delete cookies ASP.NET 核心 3.1 | 如何使用服务收集进行单元测试并使用 NullLogger 作为 ILogger 实现 - ASP.NET Core 3.1 | How to make unit tests with service collection and use NullLogger as ILogger implementation Asp.Net Core WebApi:创建单元测试 - Asp.Net Core WebApi: Create Unit Tests 在.Net Core 1.1中不再发现XUnit测试 - XUnit Tests no longer discovered in .Net Core 1.1 为 .net 6.0 创建单元测试 - create unit tests for .net 6.0 尝试创建 Mock.Of 时出错<controllercontext> () 用于 ASP.Net Core 3.1 单元测试</controllercontext> - Error trying to create Mock.Of<ControllerContext>() for ASP.Net Core 3.1 Unit Test 带有 xunit .NET Core 3.1 和 Moq 的工作单元的单元测试服务 - Unit Testing Service with Unit of Work with xunit .NET Core 3.1 and Moq Net core EF 3.1 LINQ 字符串比较不再有效 - Net core EF 3.1 LINQ string comparison no longer working
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM