Search code examples
c#.netunit-testingasp.net-mvc-5moq

How to add claims in a mock ClaimsPrincipal


I am trying to unit test my controller code which gets the information from the ClaimsPrincipal.Current. In the controller code I

public class HomeController {
    public ActionResult GetName() {
        return Content(ClaimsPrincipal.Current.FindFirst("name").Value);
    }
}

And I am trying to mock my ClaimsPrincipal with claims but I still don't have any mock value from the claim.

// Arrange
IList<Claim> claimCollection = new List<Claim>
{
    new Claim("name", "John Doe")
};

var identityMock = new Mock<ClaimsIdentity>();
identityMock.Setup(x => x.Claims).Returns(claimCollection);

var cp = new Mock<ClaimsPrincipal>();
cp.Setup(m => m.HasClaim(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
cp.Setup(m => m.Identity).Returns(identityMock.Object);

var sut = new HomeController();

var contextMock = new Mock<HttpContextBase>();
contextMock.Setup(ctx => ctx.User).Returns(cp.Object);

var controllerContextMock = new Mock<ControllerContext>();
controllerContextMock.Setup(con => con.HttpContext).Returns(contextMock.Object);
controllerContextMock.Setup(con => con.HttpContext.User).Returns(cp.Object);

sut.ControllerContext = controllerContextMock.Object;

// Act
var viewresult = sut.GetName() as ContentResult;

// Assert
Assert.That(viewresult.Content, Is.EqualTo("John Doe"));

The viewresult.Content is empty as I run the unit test. Any help if I can add the mock claim. Thanks.


Solution

  • First, you are missing this line in your test:

    Thread.CurrentPrincipal = cp.Object;  
    

    (and then cleaning it up in TearDown).

    Second, as @trailmax mentioned, mocking principal objects is impractical. In your case, ClaimsPrincipal.FindFirst (according to decompiled source) looks into private fields of its instance, that's the reason mocking didn't help.

    I prefer using two simple classes that allow me to unit test claims-based functionality:

        public class TestPrincipal : ClaimsPrincipal
        {
            public TestPrincipal(params Claim[] claims) : base(new TestIdentity(claims))
            {
            }
        }
    
        public class TestIdentity : ClaimsIdentity
        {
            public TestIdentity(params Claim[] claims) : base(claims)
            {
            }
        }
    

    then your test shrinks down to:

        [Test]
        public void TestGetName()
        {
            // Arrange
            var sut = new HomeController();
            Thread.CurrentPrincipal = new TestPrincipal(new Claim("name", "John Doe"));
    
            // Act
            var viewresult = sut.GetName() as ContentResult;
    
            // Assert
            Assert.That(viewresult.Content, Is.EqualTo("John Doe"));
        }
    

    and it now passes, I've just verified.