Search code examples
c#asp.net-coreintegration-testingmstest

Best way to setup MSTest for REST service with cookie-authentication?


Background: I am using ASP.NET Core 3.1, and integration testing a REST service that requires cookie authentication.

Candidate solution below.

Note:

  • The reason I use a vanilla Host instead of TestServer is because of the cookie requirement. When using TestServer, it provides an HttpClient for you, but the client does not pass cookies back to the server.
  • I also attempted to use a custom HttpClient with TestServer. That consistently generated a System.Net.Sockets.SocketException (No connection could be made because the target machine actively refused it.)
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 = "[email protected]",
                    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);
            }
        }
    }
}

Solution

  • HttpClient is a thin-client; it doesn't do anything unless you explicitly tell it to. In other words, it will never send the cookie for you; you must add a Cookie header to the request with the cookie value for each request. The test server "client" is just an HttpClient instance set up to proxy requests to the test server. You should use the test server, as prescribed, along with its client, and then add the Cookie header the requests you make with that.