9

I'm trying to mock the User.Identity in my Api Controller test.

This is my api method:

    [Route(Urls.CustInfo.GetCustomerManagers)]
    public HttpResponseMessage GetCustomerManagers([FromUri]int groupId = -1)
    {
        var user = User.Identity.Name;
        if (IsStaff(user) && groupId == -1)
        {
            return ErrorMissingQueryStringParameter;
        }
        ...
    }

I followed the suggestion in this post: Set User property for an ApiController in Unit Test to set the User property.

This is my test:

    [TestMethod]
    public void User_Without_Group_Level_Access_Call_GetCustomerManagers_Should_Fail()
    {
        Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("Bob", "Passport"), new[] {"managers"}); 
        var response = m_controller.GetCustomerManagers();

        Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
    }

But when the test is run, the User property is always null.

I even tried moving the line for setting the CurrentPrincipal into the api method just before calling User.Identity, but it's still null.

What am I doing wrong? If this approach doesn't work for web api 2, what's the best way to simulate/mock the User property?

Thanks!

Community
  • 1
  • 1
ozstudent
  • 381
  • 1
  • 5
  • 14

3 Answers3

18

You can set the user in ControllerContext.RequestContext.Principal:

controller.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity("Bob", "Passport"), new[] {"managers"});

Or a shorthand equivalent:

controller.User = new GenericPrincipal(new GenericIdentity("Bob", "Passport"), new[] {"managers"});
LostInComputer
  • 15,188
  • 4
  • 41
  • 49
  • 2
    After setting the Principal in this way, the UserId is not set. I need it to be set because the logic in my system-under-test is based on the Userid. E.g. `User.Identity.Name` is filled ("Bob") but not `User.Identity.GetUserId()`. – Bart Mar 03 '16 at 14:43
  • @Ben Hall It's been a while, but I don't think I did. I think my workaround was just to change the logic to use `UserName` instead of `UserId`. – Bart Nov 06 '16 at 06:35
  • @Bart I did come up with a solution. See new answer below. Might want to mark it as correct answer? – Ben Hall Nov 17 '16 at 13:14
1

Though it is an old post, but I would like to add my thought as well.

If you only want to mock User Identity no need to use any mock framework, as you cannot mock User Identity using mock framework(not atleast with moq).

Simply assign the HttpContext.current property as below

HttpContext.Current = new HttpContext(
    new HttpRequest("", "http://localhost", ""),
    new HttpResponse(null)
);

And the HttpContext.Current.User be like this

HttpContext.Current.User = 
           new GenericPrincipal(
               new GenericIdentity("name"),
               new []{"manager","Admin"}
           );

Now the HttpContext.Current.User.Identity.Name will be available in controller of web api.

If you want to mock other property or object use moq or any other framework. But mocking HttpContext is as simple as the above.

Iftikhar Ali Ansari
  • 1,650
  • 1
  • 17
  • 27
0

I did manage to solve this to actually set something to return for GetUserId() as opposed to a username given in the answer with the most votes. Apologies that oyu will have to translate from my use of FakeItEasy to the mocking tool of your choice.

Claim claim = new Claim("", userId);
ClaimsIdentity fakeClaimsIdentity = A.Fake<ClaimsIdentity>();
A.CallTo(() => fakeClaimsIdentity.FindFirst(A<string>.Ignored)).Returns(claim);

IPrincipal fakeIPrincipal = A.Fake<IPrincipal>();
A.CallTo(() => fakeIPrincipal.Identity).Returns(fakeClaimsIdentity);

var controller = new AuthorisedServicesController()
{
      User = fakeIPrincipal
};

However a better solution (if I was writing the code again) would be to abstract the actual code calling GetUserId so I can then inject an object that can make that call, this being easily mockable. A bit like a factory.

Ben Hall
  • 1,353
  • 10
  • 19