简体   繁体   English

User.Identity.GetUserId()Owin Moq单元测试

[英]User.Identity.GetUserId() Owin Moq unit test

I have ChangePassword method where I have User.Identity.GetUserId() to find UserId . 我有ChangePassword方法,其中有User.Identity.GetUserId()来查找UserId

Problem: It always return null. 问题:它总是返回null。 Don't understand why. 不明白为什么。

I read in another post that the GetUserById use below line of code to find Id . 我在另一篇文章中读到, GetUserById使用下面的代码行来查找Id I am not sure how do I mock ClaimsTypes.NameIdentifier . 我不确定如何模拟ClaimsTypes.NameIdentifier

return ci.FindFirstValue(ClaimTypes.NameIdentifier); 返回ci.FindFirstValue(ClaimTypes.NameIdentifier);

ChangePassword method (method to be unit testes) ChangePassword方法(方法为单元测试)

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 // 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 IOwinContext模拟代码

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. 您的GetPrincipal可以更新为使用声明。

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. 如果创建一个具体的TestService并连接到实际的实现,则会引起问题。 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. 您应该模拟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 根据您要测试的方法以及您在注释中指出MyController是一个抽象类,应采用以下测试

[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
        //...
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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