Testing 在ASP.NET 5/MVC 6上的AspNet.TestHost.TestServer上下文中访问单元测试内部的Cookie

Testing 在ASP.NET 5/MVC 6上的AspNet.TestHost.TestServer上下文中访问单元测试内部的Cookie,testing,cookies,owin,asp.net-core,asp.net-core-mvc,Testing,Cookies,Owin,Asp.net Core,Asp.net Core Mvc,在运行与AspNet.TestHost.TestServer集成测试的response对象中,没有简单的方法可以访问CookieContainer。Cookie必须由控制器操作设置。实现这一目标的最佳方式是什么 var client = TestServer.Create(app => { app.UseMvc(routes => routes.MapRou

在运行与
AspNet.TestHost.TestServer
集成测试的
response
对象中,没有简单的方法可以访问
CookieContainer
。Cookie必须由控制器操作设置。实现这一目标的最佳方式是什么

            var client = TestServer.Create(app =>
            {
                app.UseMvc(routes => 
                       routes.MapRoute("default", "{controller}/{action}/{id?}"));
                app.UseIdentity();
            }).CreateClient();

            var request = new HttpRequestMessage(HttpMethod.Get, "account/login");
            var response = await client.SendAsync(request);

            // how to get an access to cookie container ?????????
            // response.Cookies prop doesn't exist
            Assert.NotEmpty(response.Cookies["auth"]);
我看到的解决方案是扩展TestServer的实例,返回一个类的实例
CustomClientHandler:ClientHandler
,并覆盖在该处理程序中发送请求的整个过程,但它实际上需要更改除TestServer相对较小的代码之外的所有逻辑


有没有更好的建议如何在响应中实现对cookie的访问

我已经实现了跟踪Cookie的自定义HttpMessageHandler

它使用反射来调用实际的处理程序,只读取/设置Cookie头

class TestMessageHandler : HttpMessageHandler
{
    delegate Task<HttpResponseMessage> HandlerSendAsync(HttpRequestMessage message, CancellationToken token);

    private readonly HandlerSendAsync nextDelegate;
    private readonly CookieContainer cookies = new System.Net.CookieContainer();

    public TestMessageHandler(HttpMessageHandler next)
    {
        if(next == null) throw new ArgumentNullException(nameof(next));

        nextDelegate = (HandlerSendAsync)
                            next.GetType()
                                .GetTypeInfo()
                                .GetMethod("SendAsync", BindingFlags.NonPublic | BindingFlags.Instance)
                                .CreateDelegate(typeof(HandlerSendAsync), next);
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("Cookie", cookies.GetCookieHeader(request.RequestUri));

        var resp = await nextDelegate(request, cancellationToken).ConfigureAwait(false);

        if (resp.Headers.TryGetValues("Set-Cookie", out var newCookies))
        {
            foreach (var item in SetCookieHeaderValue.ParseList(newCookies.ToList()))
            {
                cookies.Add(request.RequestUri, new Cookie(item.Name, item.Value, item.Path));
            }
        }

        return resp;
    }
}

TestMessageHandler现在负责跟踪Cookie。

作为@Oleh响应的一个补充,您可以在不考虑.NET 4.6.1+/.NET Core等较新框架的情况下实现相同的功能

public class TestHttpClientHandler : DelegatingHandler
{
    [NotNull]
    private readonly CookieContainer cookies = new CookieContainer();

    public TestHttpClientHandler([NotNull] HttpMessageHandler innerHandler)
        : base(innerHandler) { }

    [NotNull, ItemNotNull]
    protected override async Task<HttpResponseMessage> SendAsync([NotNull] HttpRequestMessage request, CancellationToken ct)
    {
        Uri requestUri = request.RequestUri;
        request.Headers.Add(HeaderNames.Cookie, this.cookies.GetCookieHeader(requestUri));

        HttpResponseMessage response = await base.SendAsync(request, ct);

        if (response.Headers.TryGetValues(HeaderNames.SetCookie, out IEnumerable<string> setCookieHeaders))
        {
            foreach (SetCookieHeaderValue cookieHeader in SetCookieHeaderValue.ParseList(setCookieHeaders.ToList()))
            {
                Cookie cookie = new Cookie(cookieHeader.Name.Value, cookieHeader.Value.Value, cookieHeader.Path.Value);
                if (cookieHeader.Expires.HasValue)
                {
                    cookie.Expires = cookieHeader.Expires.Value.DateTime;
                }
                this.cookies.Add(requestUri, cookie);
            }
        }

        return response;
    }
}
公共类TestHttpClientHandler:DelegatingHandler
{
[非空]
私有只读CookieContainer cookies=新CookieContainer();
公共测试HttpClientHandler([NotNull]HttpMessageHandler innerHandler)
:base(innerHandler){}
[NotNull,ItemNotNull]
受保护的覆盖异步任务SendAsync([NotNull]HttpRequestMessage请求,CancellationToken ct)
{
urirequesturi=request.requestUri;
request.Headers.Add(HeaderNames.Cookie,this.cookies.GetCookieHeader(requestUri));
HttpResponseMessage response=等待base.SendAsync(请求,ct);
if(response.Headers.TryGetValues(HeaderNames.SetCookie,out IEnumerable setCookieHeaders))
{
foreach(SetCookieHeaderValue.ParseList(setCookieHeaders.ToList())中的SetCookieHeaderValue cookieHeader)
{
Cookie Cookie=新Cookie(cookieHeader.Name.Value、cookieHeader.Value.Value、cookieHeader.Path.Value);
if(cookieHeader.Expires.HasValue)
{
cookie.Expires=cookieHeader.Expires.Value.DateTime;
}
this.cookies.Add(requestUri,cookie);
}
}
返回响应;
}
}

对于dotnet核心集成测试方法,如文档中所述,您可以使用以下代码获取cookie:

public class CookieTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public CookieTests(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task GetPage_ShouldSetCookie_CookieSet()
    {
        using (var client = _factory.CreateClient())
        {
            var response = await client.GetAsync("/cookie_setting_url");
            response.EnsureSuccessStatusCode();

            //or other assertions
            Assert.True(response.Headers.TryGetValues(HeaderNames.SetCookie, out IEnumerable<string> cookies));
        }
    }
}
公共类Cookiets:IClassFixture
{
私有只读WebApplicationFactory\u工厂;
公共烹饪测试(WebApplicationFactory)
{
_工厂=工厂;
}
[事实]
公共异步任务GetPage\u ShouldSetCookie\u CookieSet()
{
使用(var client=\u factory.CreateClient())
{
var response=wait client.GetAsync(“/cookie\u setting\u url”);
response.EnsureSuccessStatusCode();
//或其他断言
True(response.Headers.TryGetValues(HeaderNames.SetCookie,out IEnumerable cookies));
}
}
}

Cookies只是标题。下面是一个从响应中获取cookie并将其添加到下一个请求的示例:即使我只执行var h=testServer.CreateHandler();_client1=新的HttpClient(h,true);任何请求都会导致:提供了无效的请求URI。请求URI必须是绝对URI或必须设置基地址。所以,我想这不适用于TestServer@DmitryGusarov下面是我使用的代码:
var-client=new-HttpClient(new-TestMessageHandler(server.CreateHandler()){BaseAddress=server.BaseAddress}
,我通常为我的所有客户机指定基址,但在答案中使用了它。这非常有效。这是一个很好的解决一个非平凡问题的方法,谢谢你的贡献!
public class CookieTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public CookieTests(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task GetPage_ShouldSetCookie_CookieSet()
    {
        using (var client = _factory.CreateClient())
        {
            var response = await client.GetAsync("/cookie_setting_url");
            response.EnsureSuccessStatusCode();

            //or other assertions
            Assert.True(response.Headers.TryGetValues(HeaderNames.SetCookie, out IEnumerable<string> cookies));
        }
    }
}