Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xunit - how to get HttpContext.User.Identity in unit tests

I added a method to my controllers to get the user-id from the JWT token in the HttpContext. In my unit tests the HttpContext is null, so I get an exception.

How can I solve the problem? Is there a way to moq the HttpContext?

Here is the method to get the user in my base controller

protected string GetUserId()
{
    if (HttpContext.User.Identity is ClaimsIdentity identity)
    {
        IEnumerable<Claim> claims = identity.Claims;
        return claims.ToList()[0].Value;
    }

    return "";
}

One of my tests look like this

[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
    //....
}
like image 239
Henning Avatar asked Nov 20 '19 09:11

Henning


2 Answers

There really is no need to have to mock the HttpContext in this particular case.

Use the DefaultHttpContext and set the members necessary to exercise the test to completion

For example

[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
    ....
}
like image 53
Nkosi Avatar answered Nov 07 '22 01:11

Nkosi


First of all, I would suggest you to use IHttpContextAccessor to access HttpContext and inject via Dependency Injection instead of using HttpContext directly. You can follow this Microsoft documentation to understand usage and injection of IHttpContextAccessor.

With the above code, your code will looks as follows to inject IHttpContextAccessor

private IHttpContextAccessor  httpContextAccessor;
public class UsergroupController(IHttpContextAccessor httpContextAccessor, ...additional parameters)
{
   this.httpContextAccessor = httpContextAccessor;
   //...additional assignments
}

Once IHttpContextAccessor is injected, you can access the Identity as this.httpContextAccessor.HttpContext.User.Identity

So the GetUserId should change as

protected string GetUserId()
{
    if (this.httpContextAccessor.HttpContext.User.Identity is ClaimsIdentity identity)
    {
        IEnumerable<Claim> claims = identity.Claims;
        return claims.ToList()[0].Value;
    }

    return "";
}

With above change, now you can easily inject the mock of IHttpContextAccessor for unit testing. You can use the below code to create the 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;
}

With the above setup, in your test method, you can inject the mock of IHttpContextAccessor while instantiating the object of UsergroupController.

like image 5
user1672994 Avatar answered Nov 07 '22 03:11

user1672994