Лучший способ настроить MSTest для службы REST с аутентификацией cook ie? - PullRequest
0 голосов
/ 20 апреля 2020

Справочная информация: я использую ASP. NET Core 3.1 и интеграционное тестирование службы REST, требующей аутентификации cook ie.

Кандидатское решение ниже.

Примечание:

  • Причина, по которой я использую ванильный хост вместо TestServer, заключается в требовании повара ie. При использовании TestServer он предоставляет вам HttpClient, но клиент не передает куки обратно на сервер.
  • Я также пытался использовать собственный HttpClient с TestServer. Это последовательно генерировало System. Net .Sockets.SocketException (Невозможно установить соединение, поскольку целевая машина активно отказывала в этом.)
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi; // Contains my Startup.cs

namespace WebApiTest
{
    [TestClass]
    public class UserTest
    {
        static IHost HttpHost;

        [ClassInitialize]
        public static async Task ClassStartup(TestContext context)
        {
            HttpHost = Host.CreateDefaultBuilder()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .Build();
            await HttpHost.StartAsync();
        }

        [ClassCleanup]
        public static async Task ClassCleanup()
        {
            await HttpHost.StopAsync();
        }

        public static HttpContent GetHttpContent(object content)
        {
            HttpContent httpContent = null;

            if (content != null)
            {
                httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
                httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            }

            return httpContent;
        }

        public static HttpClient GetCookieHttpClient()
        {
            SocketsHttpHandler handler = new SocketsHttpHandler
            {
                AllowAutoRedirect = false,
                CookieContainer = new CookieContainer(),
                UseCookies = true
            };

            return new HttpClient(handler);
        }

        [TestMethod]
        public async Task GetUserData_ReturnsSuccess()
        {
            using (HttpClient client = GetCookieHttpClient())
            {
                var credentials = new
                {
                    Email = "test@test.com",
                    Password = "password123",
                };

                HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
                response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
                Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 25 апреля 2020

Решения, основанные на предложениях Криса Пратта

После дальнейших исследований Microsoft предлагает решение для этого (WebApplicationFactory):

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;

namespace WebApiTest
{
    [TestClass]
    public class Class2
    {
        static WebApplicationFactory<Startup> Factory;
        static WebApplicationFactoryClientOptions ClientOptions;

        [ClassInitialize]
        public static async Task ClassStartup(TestContext context)
        {
            Factory = new WebApplicationFactory<Startup>();
            ClientOptions = new WebApplicationFactoryClientOptions();
            ClientOptions.AllowAutoRedirect = false;
            ClientOptions.HandleCookies = true;
            ClientOptions.BaseAddress = new Uri("http://localhost:5000");
        }

        public static HttpContent GetHttpContent(object content)
        {
            HttpContent httpContent = null;

            if (content != null)
            {
                httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
                httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            }

            return httpContent;
        }

        [TestMethod]
        public async Task GetUserData_ReturnsSuccess()
        {
            using (HttpClient client = Factory.CreateClient(ClientOptions))
            {
                var credentials = new
                {
                    Email = "test@test.com",
                    Password = "password123",
                };

                HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
                response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
                Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
            }

        }
    }
}

На случай, если вы захотите придерживайтесь TestServer, вот ручная реализация Cook ie:

using Microsoft.AspNetCore.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;

namespace WebApiTest
{
    public class CookieHttpClient : IDisposable
    {
        private static HttpContent GetHttpContent(object content)
        {
            HttpContent httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
            httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            return httpContent;
        }

        private static IEnumerable<string> GetCookieStrings(CookieCollection collection)
        {
            List<string> output = new List<string>(collection.Count);
            foreach (Cookie cookie in collection)
            {
                output.Add(cookie.Name + "=" + cookie.Value);
            }
            return output;
        }

        private HttpClient client;
        private CookieContainer container;

        public CookieHttpClient(HttpClient client)
        {
            this.client = client;
            this.container = new CookieContainer();
        }

        public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri)
        {
            return await this.SendAsync(method, uri, null);
        }

        public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri, object data)
        {
            HttpRequestMessage request = new HttpRequestMessage(method, uri);

            // Add data
            if (data != null)
            {
                request.Content = GetHttpContent(data);
            }

            // Add cookies
            CookieCollection collection = this.container.GetCookies(uri);
            if (collection.Count > 0)
            {
                request.Headers.Add("Cookie", GetCookieStrings(collection));
            }

            HttpResponseMessage response = await this.client.SendAsync(request);

            // Remember cookies before returning
            if (response.Headers.Contains("Set-Cookie"))
            {
                foreach (string s in response.Headers.GetValues("Set-Cookie"))
                {
                    this.container.SetCookies(uri, s);
                }
            }

            return response;
        }

        public void Dispose()
        {
            this.client.Dispose();
        }
    }

    [TestClass]
    public class Class1
    {
        static TestServer TestServer;

        [ClassInitialize]
        public static async Task ClassStartup(TestContext context)
        {
            IWebHostBuilder builder = new WebHostBuilder()
                .UseStartup<Startup>();
            TestServer = new TestServer(builder);
        }

        [TestMethod]
        public async Task GetUserData_ReturnsSuccess()
        {
            using (CookieHttpClient client = new CookieHttpClient(TestServer.CreateClient()))
            {
                var credentials = new
                {
                    Email = "test@test.com",
                    Password = "password123",
                };

                HttpResponseMessage response = await client.SendAsync(HttpMethod.Post, new Uri("http://localhost:5000/api/auth/login"), credentials);
                response = await client.SendAsync(HttpMethod.Get, new Uri("http://localhost:5000/api/users/" + credentials.Email));
                Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
            }
        }
    }
}
0 голосов
/ 20 апреля 2020

HttpClient - тонкий клиент; это ничего не делает, если вы явно не скажете это. Другими словами, он никогда не отправит вам повара ie; Вы должны добавить заголовок Cookie к запросу со значением cook ie для каждого запроса . Тестовый сервер "client" - это просто экземпляр HttpClient, настроенный для запросов прокси к тестовому серверу. Вы должны использовать тестовый сервер, как это предписано, вместе с его клиентом, а затем добавить заголовок Cookie запросов, которые вы делаете с этим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...