【问题标题】:xunit - how to get HttpContext.User.Identity in unit testsxunit - 如何在单元测试中获取 HttpContext.User.Identity
【发布时间】:2019-11-20 09:16:22
【问题描述】:

我向我的控制器添加了一个方法来从 HttpContext 中的 JWT 令牌中获取用户 ID。在我的单元测试中,HttpContextnull,所以我得到了一个异常。

我该如何解决这个问题?有没有办法将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
    //....
}

【问题讨论】:

    标签: c# unit-testing .net-core xunit


    【解决方案1】:

    在这种特殊情况下,确实没有必要模拟 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
        ....
    }
    

    【讨论】:

      【解决方案2】:

      首先,我建议你使用IHttpContextAccessor访问HttpContext并通过Dependency Injection注入,而不是直接使用HttpContext。你可以按照这个微软文档来了解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 的模拟。

      【讨论】:

      • 控制器已经可以访问HttpContextIHttpContextAccessor 应该在控制器之外使用。
      猜你喜欢
      • 2018-12-26
      • 2020-06-14
      • 2018-12-11
      • 2020-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-01
      • 1970-01-01
      相关资源
      最近更新 更多