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

时间:2016-08-17 11:39:07

标签: c# unit-testing asp.net-web-api moq owin

我有ChangePassword方法,我User.Identity.GetUserId()找到UserId

  

问题:它总是返回null。不明白为什么。

我在另一篇文章中读到,GetUserById使用以下代码行来查找Id。我不知道如何模仿ClaimsTypes.NameIdentifier

  

返回ci.FindFirstValue(ClaimTypes.NameIdentifier);

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();
}

单元测试

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

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;
}

1 个答案:

答案 0 :(得分:6)

您的 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;
}

以下是一个示例,说明了上述方法的工作原理。

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);
        }
    }
}

您有一些设计问题。如果连接到实际实现,创建具体的TestService将导致问题。这成为一个集成测试。摘要也表示依赖。

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();
    }

}

此外,您不应该模拟被测系统。你应该模拟SUT的依赖关系。根据您要测试的方法以及您在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
        //...
    }
}