简体   繁体   中英

User.Identity.GetUserId() Owin Moq unit test

I have ChangePassword method where I have User.Identity.GetUserId() to find UserId .

Problem: It always return null. Don't understand why.

I read in another post that the GetUserById use below line of code to find Id . I am not sure how do I mock ClaimsTypes.NameIdentifier .

return ci.FindFirstValue(ClaimTypes.NameIdentifier);

ChangePassword method (method to be unit testes)

public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword)
{

    _tstService = new TestService();
    IdentityResult result = await _tstService.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);

    if (!result.Succeeded)
    {
        return GetErrorResult(result);
    }

    return Ok();
}

Unit Test

var mock = new Mock<MyController>();
mock.CallBase = true;
var obj = mock.Object;

obj.ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() };
obj.Request.SetOwinContext(CommonCodeHelper.mockOwinContext());

IPrincipal user = GetPrincipal();
obj.ControllerContext.RequestContext.Principal = user;
var result = await obj.ChangePassword(dto);

//GetPrincipal

public static IPrincipal GetPrincipal()
{
    var user = new Mock<IPrincipal>();
    var identity = new Mock<IIdentity>();
    identity.Setup(x => x.Name).Returns("User1@Test.com");
    identity.Setup(p => p.IsAuthenticated).Returns(true);

    user.Setup(x => x.Identity).Returns(identity.Object);
    Thread.CurrentPrincipal = user.Object;
    return user.Object;
}

IOwinContext mocking code

public static IOwinContext mockOwinContext()
{
    var owinMock = new Mock<IOwinContext>();
    owinMock.Setup(o => o.Authentication.User).Returns(new ClaimsPrincipal());
    owinMock.Setup(o => o.Request).Returns(new Mock<OwinRequest>().Object);
    owinMock.Setup(o => o.Response).Returns(new Mock<OwinResponse>().Object);
    owinMock.Setup(o => o.Environment).Returns(new Dictionary<string, object> { { "key1", 123 } });
    var traceMock = new Mock<TextWriter>();
    owinMock.Setup(o => o.TraceOutput).Returns(traceMock.Object);

    var userStoreMock = new Mock<IUserStore<IfsUser>>();
    userStoreMock.Setup(s => s.FindByIdAsync("User1@ifstoolsuite.com")).ReturnsAsync(new IfsUser
    {
        Id = "User1@test.com",
        FirstName = "Test",
        LastName = "User1",
        Email = "User1@test.com",
        UserName = "User1@test.com",
    });
    var applicationUserManager = new IfsUserManager(userStoreMock.Object);
    owinMock.Setup(o => o.Get<IfsUserManager>(It.IsAny<string>())).Returns(applicationUserManager);
    return owinMock.Object;
}

Your GetPrincipal can be updated to use claims.

public static IPrincipal GetPrincipal() {
    //use an actual identity fake
    var username = "User1@Test.com";
    var identity = new GenericIdentity(username, "");
    //create claim and add it to indentity
    var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
    identity.AddClaim(nameIdentifierClaim);

    var user = new Mock<IPrincipal>();
    user.Setup(x => x.Identity).Returns(identity);
    Thread.CurrentPrincipal = user.Object;
    return user.Object;
}

Here is an example that shows how the above approach works.

public partial class MiscUnitTests {
    [TestClass]
    public class IdentityTests : MiscUnitTests {
        Mock<IPrincipal> mockPrincipal;
        string username = "test@test.com";

        [TestInitialize]
        public override void Init() {
            //Arrange                
            var identity = new GenericIdentity(username, "");
            var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
            identity.AddClaim(nameIdentifierClaim);

            mockPrincipal = new Mock<IPrincipal>();
            mockPrincipal.Setup(x => x.Identity).Returns(identity);
            mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
        }

        [TestMethod]
        public void Should_GetUserId_From_Identity() {

            var principal = mockPrincipal.Object;

            //Act
            var result = principal.Identity.GetUserId();

            //Asserts
            Assert.AreEqual(username, result);
        }

        [TestMethod]
        public void Identity_Should_Be_Authenticated() {

            var principal = mockPrincipal.Object;

            //Asserts
            Assert.IsTrue(principal.Identity.IsAuthenticated);
        }
    }
}

You have some design issues. Creating a concrete TestService will cause problems if it as connecting to an actual implementation. That becomes an integration test. Abstract that dependency as well.

public interface ITestService {
    Task<IdentityResult> ChangePassword(string userId, string oldPassword, string newPassword);
}

public abstract class MyController : ApiController {
    private ITestService service;

    protected MyController(ITestService service) {
        this.service = service;
    }

    public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword) {

        IdentityResult result = await service.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);

        if (!result.Succeeded) {
            return GetErrorResult(result);
        }

        return Ok();
    }

}

Also you should not mock the System under test. You should mock the dependencies of the SUT. Based on your method to be tested and what you indicated in the comments that MyController is an abstract class, the following test should apply

[TestClass]
public class MyControllerTests {
    public class FakeController : MyController { 
        public FakeController(ITestService service) : base(service) { }
    }

    [TestMethod]
    public void TestMyController() {
        //Arrange
        var mockService = new Mock<ITestService>();
        mockService
            .Setup(m => m.ChangePassword(....))
            .ReturnsAsync(....);
        var controller = new FakeController(mockService.Object);

        //Set a fake request. If your controller creates responses you will need this
        controller.Request = new HttpRequestMessage {
            RequestUri = new Uri("http://localhost/api/my")
        };
        controller.Configuration = new HttpConfiguration();
        controller.User = GetPrincipal();

        //Act
        var result = await controller.ChangePassword("NewPassword", "OldPassword");

        //Assert
        //...
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM