xunit - 如何在单元测试中获得 HttpContext.User.Identity
xunit - how to get HttpContext.User.Identity in unit tests
我向我的控制器添加了一个方法来从 HttpContext
中的 JWT 令牌获取用户 ID。在我的单元测试中,HttpContext
是 null,所以我得到一个异常。
我该如何解决这个问题? HttpContext
有最小起订量的方法吗?
这是在我的基本控制器中获取用户的方法
protected string GetUserId()
{
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
我的一个测试是这样的
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup)
{
// Arrange
UsergroupController controller = new UsergroupController(context, mapper);
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
//....
}
首先,我建议您使用IHttpContextAccessor
访问HttpContext
并通过Dependency Injection
注入,而不是直接使用HttpContext
。您可以按照此 Microsoft 文档了解 IHttpContextAccessor
.
的用法和注入
使用上面的代码,您的代码将如下所示注入 IHttpContextAccessor
private IHttpContextAccessor httpContextAccessor;
public class UsergroupController(IHttpContextAccessor httpContextAccessor, ...additional parameters)
{
this.httpContextAccessor = httpContextAccessor;
//...additional assignments
}
注入 IHttpContextAccessor
后,您可以访问身份 this.httpContextAccessor.HttpContext.User.Identity
所以 GetUserId
应该变成
protected string GetUserId()
{
if (this.httpContextAccessor.HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
通过上述更改,现在您可以轻松地注入 IHttpContextAccessor
的 mock 进行单元测试。您可以使用以下代码创建模拟:
private static ClaimsPrincipal user = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[] { new Claim("MyClaim", "MyClaimValue") },
"Basic")
);
private static Mock<IHttpContextAccessor> GetHttpContextAccessor()
{
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock.Setup(h => h.HttpContext.User).Returns(user);
return httpContextAccessorMock;
}
通过上面的设置,在你的测试方法中,你可以在实例化UsergroupController
的对象时注入IHttpContextAccessor
的mock。
在这种特殊情况下,确实没有必要模拟 HttpContext
。
使用DefaultHttpContext
并设置完成测试所需的成员
例如
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup) {
// Arrange
//...
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity); //add claims as needed
//...then set user and other required properties on the httpContext as needed
var httpContext = new DefaultHttpContext() {
User = contextUser;
};
//Controller needs a controller context to access HttpContext
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
UsergroupController controller = new UsergroupController(context, mapper) {
ControllerContext = controllerContext,
};
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
....
}
作为@Nkosi 评论的跟进:
不需要 DI 上下文,因为您可以在测试期间像这样设置它:
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = contextUser }
};
我向我的控制器添加了一个方法来从 HttpContext
中的 JWT 令牌获取用户 ID。在我的单元测试中,HttpContext
是 null,所以我得到一个异常。
我该如何解决这个问题? HttpContext
有最小起订量的方法吗?
这是在我的基本控制器中获取用户的方法
protected string GetUserId()
{
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
我的一个测试是这样的
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup)
{
// Arrange
UsergroupController controller = new UsergroupController(context, mapper);
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
//....
}
首先,我建议您使用IHttpContextAccessor
访问HttpContext
并通过Dependency Injection
注入,而不是直接使用HttpContext
。您可以按照此 Microsoft 文档了解 IHttpContextAccessor
.
使用上面的代码,您的代码将如下所示注入 IHttpContextAccessor
private IHttpContextAccessor httpContextAccessor;
public class UsergroupController(IHttpContextAccessor httpContextAccessor, ...additional parameters)
{
this.httpContextAccessor = httpContextAccessor;
//...additional assignments
}
注入 IHttpContextAccessor
后,您可以访问身份 this.httpContextAccessor.HttpContext.User.Identity
所以 GetUserId
应该变成
protected string GetUserId()
{
if (this.httpContextAccessor.HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
通过上述更改,现在您可以轻松地注入 IHttpContextAccessor
的 mock 进行单元测试。您可以使用以下代码创建模拟:
private static ClaimsPrincipal user = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[] { new Claim("MyClaim", "MyClaimValue") },
"Basic")
);
private static Mock<IHttpContextAccessor> GetHttpContextAccessor()
{
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock.Setup(h => h.HttpContext.User).Returns(user);
return httpContextAccessorMock;
}
通过上面的设置,在你的测试方法中,你可以在实例化UsergroupController
的对象时注入IHttpContextAccessor
的mock。
在这种特殊情况下,确实没有必要模拟 HttpContext
。
使用DefaultHttpContext
并设置完成测试所需的成员
例如
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup) {
// Arrange
//...
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity); //add claims as needed
//...then set user and other required properties on the httpContext as needed
var httpContext = new DefaultHttpContext() {
User = contextUser;
};
//Controller needs a controller context to access HttpContext
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
UsergroupController controller = new UsergroupController(context, mapper) {
ControllerContext = controllerContext,
};
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
....
}
作为@Nkosi 评论的跟进: 不需要 DI 上下文,因为您可以在测试期间像这样设置它:
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = contextUser }
};