我正在尝试对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
。我可以强迫它们在同一个线程上吗?
答案 0 :(得分:3)
我可以强制将它们放在同一个帖子上吗?
你不能。
更好的问题是“如何将Thread.CurrentPrincipal
传递给正在执行请求的任何线程”? 是这个问题的答案。
Thread.CurrentPrincipal
很奇怪。事实上,我建议你根本不要使用它;请改用HttpContext.User
。但如果你愿意,你可以通过理解这些要点来实现它:
HttpContext.User
由ASP.NET SynchronizationContext
传播。Thread.CurrentPrincipal
,HttpContext.User
就会被SynchronizationContext
覆盖。不幸的是,您当前的测试在几个关键点上存在缺陷:
Thread.CurrentPrincipal
的值未定义。HttpContext
(或ASP.NET SynchronizationContext
),这会干扰主要用户的流动。要完全测试授权,您需要进行集成测试。
答案 1 :(得分:2)
您实际遇到的是await的行为。当您退出await时,Await会将主体重置为您进入等待时的状态。因此,当你调用await invoker.SendAsync时没有当前主体,在等待该调用之后将没有当前主体。
但是,您的测试处理程序应该看到正确的主体。你可以做的是让你的测试处理程序将当前主体存储在其SendAsync实现中,将其作为公共属性公开,然后让测试断言测试处理程序看到它应该的主体。这应该很好,这应该是你关心的行为。