Reputation: 556
How do you stub User.Identity.GetUserId() in ASP MVC 5 (Microsoft.AspNet.Identity) for a unit test of a MVC5 Controller? GetUserId() is an extension, so I can't mock it directly. And I need to assign the Id before the test. It seems that you have to create a Claim and assign it to a GenericIdentity. But it seems like a lot to do for a unit test. Do you know of any alternatives?
Upvotes: 9
Views: 4323
Reputation: 153
You can create a fake principal and/or identity that inherits from the objects in MvcContrib.TestHelper.Fakes
Then you can do something like this:
using (var userManager = _userManagerFactory.CreateUserManager())
{
var userId = Guid.NewGuid().ToString();
var result = userManager.CreateAsync(new IdentityUser() { Id = userId, UserName = "userName" }, "password").Result;
Assert.IsTrue(result.Succeeded, "failed to create user through usermanager");
var fakePrincipal = new FakePrincipal(new FakeClaimIdentity(userId, "userName"), new[] { "SomeRole" });
Assert.IsTrue(fakePrincipal.Identity.IsAuthenticated, "fake user did not come back authenticated");
}
The class I used to make that fake claims identity is this:
using System.Collections.Generic;
using System.Security.Claims;
using MvcContrib.TestHelper.Fakes;
namespace Tests.Unit.Fakes
{
public class FakeClaimIdentity : ClaimsIdentity
{
private readonly FakeIdentity _fakeIdentity;
public FakeClaimIdentity(string id, string userName) : base(userName)
{
_fakeIdentity = new FakeIdentity(userName);
base.AddClaims(new List<Claim>
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", userName),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", id)
});
}
public override bool IsAuthenticated
{
get { return _fakeIdentity.IsAuthenticated; }
}
public override string Name
{
get { return _fakeIdentity.Name; }
}
public override string AuthenticationType
{
get
{
return _fakeIdentity.AuthenticationType;
}
}
}
}
This way the IsAuthenticated portion can also be tested (if you care about the validity of the identity).
Upvotes: 0
Reputation: 556
Thank you very much for the idea. I am using NSubstitute. I don’t have neither Microsoft Fakes nor JustMock. So I ended stuffing claims directly to the GenericIdentity so I could control the value of the UserId:
string username = "username";
string userid = Guid.NewGuid().ToString("N"); //could be a constant
List<Claim> claims = new List<Claim>{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", username),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userid)
};
var genericIdentity = new GenericIdentity("");
genericIdentity.AddClaims(claims);
var genericPrincipal = new GenericPrincipal(genericIdentity, new string[] { "Asegurado" });
controllerContext.HttpContext.User = genericPrincipal;
Upvotes: 12
Reputation: 11795
Ready for some pain?
It's going to depend greatly on which mocking framework you are using. Based on my assumption that you're using Moq then you're somewhat out of luck since Moq doesn't do a great job of this. The reason being that Moq can't mock extension methods ( Actually it can but you don't/shouldn't want to do it :-) )
If you're using another mocking framework like JustMock then your mileage may vary. According to this, it's a damn sight easier than what is to follow.
The easiest way I've found (which is still far from easy) is to use Microsoft Fakes
Most of the new identity stuff is written using extension interfaces. I can't for the life of me figure out why they thought this would be a good idea but there you are.
Here's what you'll need to do.
Start by creating a Fake assembly for Microsoft.AspNet.Identity
. You can do this by right clicking the assembly and choosing 'Add Fakes Assembly'. This essentially walks the assembly and created shims and stubs for everything it finds there. What you want to do is shim the call to GetUserId
,.. and actually that's not too hard.
Then you've got to do the regular mocking of ControllerContext
which you are, by the sounds of it quite familiar.
The following code will do what you're looking for I think -
using (var context = ShimsContext.Create())
{
Microsoft.AspNet.Identity.Fakes
.ShimIdentityExtensions.GetUserIdIIdentity =
(i) => "Mr. T";
var fakeHttpContext = new Mock<HttpContextBase>();
var fakeIdentity = new GenericIdentity("User");
var principal = new GenericPrincipal(fakeIdentity, null);
fakeHttpContext.Setup(t => t.User).Returns(principal);
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(t => t.HttpContext)
.Returns(fakeHttpContext.Object);
var sut = new HomeController();
sut.ControllerContext = controllerContext.Object;
var result = sut.YourTestAction();
Assert.True(result.WhatYouCareAbout);
}
Of course, it doesn't have to be Mr T.
HTH
Upvotes: 4