Как получить доступ к HttpContext внутри unit test в ASP.NET 5/MVC 6

Предположим, что я устанавливаю значение в контексте http в моем промежуточном программном обеспечении. Например, HttpContext.User.

Как проверить http-контекст в unit test. Вот пример того, что я пытаюсь сделать

Middleware

public class MyAuthMiddleware
{
    private readonly RequestDelegate _next;

    public MyAuthMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        context.User = SetUser(); 
        await next(context);
    }
}

Test

[Fact]
public async Task UserShouldBeAuthenticated()
{
    var server = TestServer.Create((app) => 
    {
        app.UseMiddleware<MyAuthMiddleware>();
    });

    using(server)
    {
        var response = await server.CreateClient().GetAsync("/");
        // After calling the middleware I want to assert that 
        // the user in the HttpContext was set correctly
        // but how can I access the HttpContext here?
    }
}

Ответ 1

Ниже приведены два подхода, которые вы можете использовать:

// Directly test the middleware itself without setting up the pipeline
[Fact]
public async Task Approach1()
{
    // Arrange
    var httpContext = new DefaultHttpContext();
    var authMiddleware = new MyAuthMiddleware(next: (innerHttpContext) => Task.FromResult(0));

    // Act
    await authMiddleware.Invoke(httpContext);

    // Assert
    // Note that the User property on DefaultHttpContext is never null and so do
    // specific checks for the contents of the principal (ex: claims)
    Assert.NotNull(httpContext.User);
    var claims = httpContext.User.Claims;
    //todo: verify the claims
}

[Fact]
public async Task Approach2()
{
    // Arrange
    var server = TestServer.Create((app) =>
    {
        app.UseMiddleware<MyAuthMiddleware>();

        app.Run(async (httpContext) =>
        {
            if(httpContext.User != null)
            {
                await httpContext.Response.WriteAsync("Claims: "
                    + string.Join(
                        ",",
                        httpContext.User.Claims.Select(claim => string.Format("{0}:{1}", claim.Type, claim.Value))));
            }
        });
    });

    using (server)
    {
        // Act
        var response = await server.CreateClient().GetAsync("/");

        // Assert
        var actual = await response.Content.ReadAsStringAsync();
        Assert.Equal("Claims: ClaimType1:ClaimType1-value", actual);
    }
}

Ответ 2

Версия RC1 asp.net 5/MVC6 позволяет установить HttpContext вручную в Unit Tests, что является удивительным!

        DemoController demoController = new DemoController();
        demoController.ActionContext = new ActionContext();
        demoController.ActionContext.HttpContext = new DefaultHttpContext();
        demoController.HttpContext.Session = new DummySession();

Класс DefaultHttpContext предоставляется платформой. DummySession может быть простым классом, который реализует класс ISession. Это упрощает многое, потому что больше не нужно насмехаться.

Ответ 3

Было бы лучше, если бы вы unit test ваш класс промежуточного ПО изолированы от остальной части вашего кода.

Так как класс HttpContext является абстрактным классом, вы можете использовать фальшивую фреймворк вроде Moq (добавив "Moq": "4.2.1502.911", в качестве зависимости от вашего файла project.json), чтобы убедиться, что свойство пользователя установлено.

Например, вы можете написать следующий тест, который проверяет, что ваша функция промежуточного программного обеспечения Invoke устанавливает свойство пользователя в httpContext и вызывает следующее промежуточное программное обеспечение:

[Fact]
public void MyAuthMiddleware_SetsUserAndCallsNextDelegate()
{
    //Arrange
    var httpContextMock = new Mock<HttpContext>()
            .SetupAllProperties();
    var delegateMock = new Mock<RequestDelegate>();
    var sut = new MyAuthMiddleware(delegateMock.Object);

    //Act
    sut.Invoke(httpContextMock.Object).Wait();

    //Assert
    httpContextMock.VerifySet(c => c.User = It.IsAny<ClaimsPrincipal>(), Times.Once);
    delegateMock.Verify(next => next(httpContextMock.Object), Times.Once);
}

Затем вы можете написать дополнительные тесты для проверки того, что пользователь имеет ожидаемые значения, так как вы сможете получить заданный объект User с помощью httpContextMock.Object.User:

Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles

Ответ 4

Взгляните на это сообщение:

Настройка HttpContext.Current.Session в unit test

Я думаю, что вам нужно это.

public static HttpContext FakeHttpContext(string url)
{
    var uri = new Uri(url);
    var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
                                        uri.Query.TrimStart('?'));
    var stringWriter = new StringWriter();
    var httpResponse = new HttpResponse(stringWriter);
    var httpContext = new HttpContext(httpRequest, httpResponse);

    var sessionContainer = new HttpSessionStateContainer("id",
                                    new SessionStateItemCollection(),
                                    new HttpStaticObjectsCollection(),
                                    10, true, HttpCookieMode.AutoDetect,
                                    SessionStateMode.InProc, false);

    SessionStateUtility.AddHttpSessionStateToContext(
                                         httpContext, sessionContainer);

    return httpContext;
}

Затем вы можете использовать его как:

request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
HttpContextFactory.Current.Request.Headers.Add(key, value);