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

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

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

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

我在另一篇文章中读到,GetUserById 使用下面的代码行来查找 Id。我不确定如何模拟 ClaimsTypes.NameIdentifier

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

//获取主体

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

最佳答案

您的 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
        //...
    }
}

关于c# - User.Identity.GetUserId() Owin Moq 单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38995597/

相关文章:

c# - Windows Phone 8.0如何切换语言

c# - 使用 HashSet.orderBy() 函数

C#:反射实例化对象会引发转换错误?

asp.net - 使用 ClientId 和 ClientSecret 进行 Web API 授权

c# - 关于 asp.net 中最大池大小的问题

c# - Xamarin.Android 点击手势和长按手势不能一起工作

exception-handling - http CreateErrorResponse 可以返回复杂的对象内容吗?

c# - 调整 MVC 4 WebApi XmlSerializer 丢失 nameSpace

java - mockito 中的动态链接 "thenReturn"

perl - 调用 Test::Output::stdout_like() 时可以在代码引用中包含参数吗