如何在 ASP.NET 5 / MVC 6 中的单元测试中访问 HttpContext
How to access HttpContext inside a unit test in ASP.NET 5 / MVC 6
假设我在中间件的 http 上下文中设置了一个值。例如 HttpContext.User.
如何在我的单元测试中测试 http 上下文。这是我正在尝试做的一个例子
中间件
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);
}
}
测试
[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?
}
}
看看这个post:
我想你需要的就是这个
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);
以下是您可以使用的两种方法:
// 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);
}
}
最好将中间件 class 与其余代码隔离开来进行单元测试。
由于 HttpContext
class 是一个抽象 class,您可以使用像 Moq
这样的模拟框架(添加 "Moq": "4.2.1502.911",
作为对 "Moq": "4.2.1502.911",
的依赖=25=] 文件)以验证用户 属性 已设置。
例如,您可以编写以下测试来验证您的中间件 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);
}
然后您可以编写额外的测试来验证用户是否具有预期值,因为您将能够使用 httpContextMock.Object.User
:
获取设置的用户对象
Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles
asp.net5/MVC6 的 RC1 版本使得在单元测试中手动设置 HttpContext 成为可能,太棒了!
DemoController demoController = new DemoController();
demoController.ActionContext = new ActionContext();
demoController.ActionContext.HttpContext = new DefaultHttpContext();
demoController.HttpContext.Session = new DummySession();
DefaultHttpContext class 由平台提供。
DummySession 可以只是实现 ISession class 的简单 class。这大大简化了事情,因为不再需要模拟。
假设我在中间件的 http 上下文中设置了一个值。例如 HttpContext.User.
如何在我的单元测试中测试 http 上下文。这是我正在尝试做的一个例子
中间件
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);
}
}
测试
[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?
}
}
看看这个post:
我想你需要的就是这个
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);
以下是您可以使用的两种方法:
// 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);
}
}
最好将中间件 class 与其余代码隔离开来进行单元测试。
由于 HttpContext
class 是一个抽象 class,您可以使用像 Moq
这样的模拟框架(添加 "Moq": "4.2.1502.911",
作为对 "Moq": "4.2.1502.911",
的依赖=25=] 文件)以验证用户 属性 已设置。
例如,您可以编写以下测试来验证您的中间件 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);
}
然后您可以编写额外的测试来验证用户是否具有预期值,因为您将能够使用 httpContextMock.Object.User
:
Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles
asp.net5/MVC6 的 RC1 版本使得在单元测试中手动设置 HttpContext 成为可能,太棒了!
DemoController demoController = new DemoController();
demoController.ActionContext = new ActionContext();
demoController.ActionContext.HttpContext = new DefaultHttpContext();
demoController.HttpContext.Session = new DummySession();
DefaultHttpContext class 由平台提供。 DummySession 可以只是实现 ISession class 的简单 class。这大大简化了事情,因为不再需要模拟。