2

我正在尝试对 DelegateHandler 的实现进行单元测试。我的简化实现:

public class FooHandler
    : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Thread.CurrentPrincipal = new GenericPrincipal(
            new GenericIdentity("Vegard"), new[] { "A", "B" });

        return await base.SendAsync(request, cancellationToken);
    }
}

当我尝试对此进行单元测试时,我会这样做:

public class TestHandler : DelegatingHandler
{
    private readonly Func<HttpRequestMessage,
        CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
    public TestHandler()
    {
        _handlerFunc = (r, c) => Return(HttpStatusCode.OK);
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return _handlerFunc(request, cancellationToken);
    }

    public static Task<HttpResponseMessage> Return(HttpStatusCode status)
    {
        return Task.Factory.StartNew(
            () => new HttpResponseMessage(status));
    }
}

[TestMethod]
public async Task SendAsync_CorrectTokens_IsAuthorized()
{
    var message = new HttpRequestMessage(HttpMethod.Get, "http://www.test.com");

    var handler = new AuthorizationHeaderHandler
        {
            InnerHandler = new TestHandler()
        };

    var invoker = new HttpMessageInvoker(handler);
    var result = await invoker.SendAsync(message, new CancellationToken());

    Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
    Assert.IsTrue(Thread.CurrentPrincipal.Identity.IsAuthenticated); // fails
    Assert.AreEqual("Vegard", Thread.CurrentPrincipal.Identity.Name); // fails
}

我的猜测是发生这种情况是因为在单独的线程上HttpMessageInvoker运行。DelegateHandler我可以强制这些在同一个线程上吗?

4

2 回答 2

3

我可以强制这些在同一个线程上吗?

你不能。

一个更好的问题是“我如何流向Thread.CurrentPrincipal执行请求的任何线程”?这个问题有一个答案。

Thread.CurrentPrincipal在 ASP.NET 中很奇怪。事实上,我建议你根本不要使用它;改为使用HttpContext.User。但是,如果您愿意,可以通过了解以下几点来使其工作:

  1. HttpContext.User由 ASP.NET 流动SynchronizationContext
  2. Thread.CurrentPrincipalHttpContext.User每当线程进入 ASP.NET 请求时,都会被覆盖SynchronizationContext

不幸的是,您当前的测试在几个关键点上存在缺陷:

  • 请求完成后, 的值Thread.CurrentPrincipal未定义。
  • 当前运行测试的方式没有HttpContext(或 ASP.NET SynchronizationContext),这会干扰主要用户的流动。

要全面测试授权,您需要进行集成测试。

另请参阅我对这个问题的回答

于 2013-06-10T13:43:23.110 回答
2

您实际遇到的是等待的行为。当您退出等待时,等待将把主体重置为您进入等待时的状态。因此,由于在您调用 await invoker.SendAsync 时没有当前主体,因此在您等待该调用后将没有当前主体。

但是,您的测试处理程序应该看到正确的主体。您可以做的是让您的测试处理程序将当前主体存储在其 SendAsync 实现中,将其公开为公共属性,然后让您的测试断言测试处理程序看到了它应该看到的主体。那应该可以正常工作,并且应该是您关心的行为。

于 2013-06-10T13:39:13.717 回答