How to add claims in a mock ClaimsPrincipal

36,465

Solution 1

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.

Solution 2

You don't need to mock ClaimsPrincipal it has no outside dependencies and you can created it un-mocked:

var claims = new List<Claim>() 
{ 
    new Claim(ClaimTypes.Name, "username"),
    new Claim(ClaimTypes.NameIdentifier, "userId"),
    new Claim("name", "John Doe"),
};
var identity = new ClaimsIdentity(claims, "TestAuthType");
var claimsPrincipal = new ClaimsPrincipal(identity);

And I'm not sure what you are testing here. Certainly "John Doe" will not be part of viewResult.Content because it is never been set to this.

Share:
36,465

Related videos on Youtube

Henry
Author by

Henry

Updated on October 24, 2020

Comments

  • Henry
    Henry over 3 years

    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.

  • Henry
    Henry almost 8 years
    Thanks! I think I was making it complicated. So when do we actually need to mock ClaimsPrincipal? Since when I check on google, many people is mocking ClaimsPrincipal. Such as this, stackoverflow.com/questions/14190066/….
  • felix-b
    felix-b almost 8 years
    You're welcome :) in that answer, the assumption that SUT will only call HasClaim method, is too fragile. If one day SUT (maybe 3rd party code) needs to access additional members of the principal, the test will break. I often prefer manually subclassing dependencies and implementing them in a "test way", while I ensure they still behave consistently.
  • Gonzalo Méndez
    Gonzalo Méndez almost 7 years
    Brilliant! Worked perfectly for me. Thank you!
  • Shawn
    Shawn over 5 years
    Can't vote this up enough, should be the accepted answer!
  • aruno
    aruno over 5 years
    Glad this is the answer. I wouldn't want to have to mock this referencesource.microsoft.com/#mscorlib/System/security/clai‌​ms/…
  • Jrow
    Jrow about 5 years
    You are my savior today
  • Ronald Abellano
    Ronald Abellano over 4 years
    You are the hero.
  • dEv93
    dEv93 almost 2 years
    No human has ever been more attractive, thank you for this lovely answer :)