简体   繁体   English

Moq用户管理器缺少IUserEmailStore

[英]Moq usermanager missing IUserEmailStore

what i am testing. 我正在测试什么。

This is an identity server project with a login to federated gateway. 这是一个具有登录联合网关的身份服务器项目。 I do not control this gateway and am having issues with them not returning the proper claims back to me that i need to verify the users logins. 我没有控制这个网关,并且他们遇到问题而没有将正确的声明返回给我,我需要验证用户登录。 I would like to be able to test that i can handle these errors. 我希望能够测试我能处理这些错误。

For example email claim is missing without that i can not login a user. 例如,电子邮件声明丢失,但我无法登录用户。

I have created a test that tests the email claim is missing returns an error.(Works fine) 我创建了一个测试,测试电子邮件声明丢失返回错误。(工作正常)

Now I am trying to test the other side of things. 现在我试图测试事物的另一面。 If the claims are in fact there it should return the user that matches to the claims returned. 如果声明实际上在那里它应该返回与返回的声明匹配的用户。

The method we are testing 我们正在测试的方法

public static async Task<(ApplicationUser user, string provider, string providerUserUserName, IEnumerable<Claim> claims, string message)> FindUserFromExternalProvider(AuthenticateResult result, UserManager<ApplicationUser> userManager, ILogger<SegesExternalController> logger)
    {
        var externalUser = result.Principal;

        // try to determine the unique id of the external user (issued by the provider)
        var eMailClaim = externalUser.FindFirst(SegesSettingsConstants.SegesEmailClaimName);

        if(eMailClaim == null) return (null, null, null, null, $"{SegesSettingsConstants.SegesEmailClaimName} claim not found.");

        // remove the user id claim so we don't include it as an extra claim if/when we provision the user
        var claims = externalUser.Claims.ToList();
        claims.LogSegesClaims(logger);

        claims.Remove(eMailClaim);
        // Should we remove more claims
        var provider = result.Properties.Items["scheme"];
        var providerUserUserName = eMailClaim.Value;

        var user = await userManager.FindByEmailAsync(providerUserUserName);  // Test Breaks here

        return (user, provider, providerUserUserName, claims, null);
    }

Test 测试

[Fact]
public async void Federated_login_with_email_claim_return_no_error()
    {
        // Arrange

        var principal = new ClaimsPrincipal();
        principal.AddIdentity(new ClaimsIdentity(
            new Claim[] {
                new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "Testbruger til André"),
                new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", @"PROD\Salg43"),
                new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode", "8200"),
                new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality", "Aarhus N"),
                new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "test@email.com"),
            },
            "FakeScheme"));
        var authenticateResult = AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties() { Items = { { "scheme", "fed" } } }, "FakeScheme"));


        var exprectUser = new ApplicationUser()
        {
            UserName = "test@email.com",
            NormalizedUserName = "TEST@EMAIL.COM",
            NormalizedEmail = "TEST@EMAIL.COM",
            Email = "test@email.com",
            Id = 123,
            EmailConfirmed = true
        };

        var mockEmailStore = new Mock<IUserEmailStore<ApplicationUser>>();
        var mockQueryableUserStore = new Mock<IQueryableUserStore<ApplicationUser>>();

        var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
        mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
        var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null,  null, null, null, null, null);


        var logger = new Logger<ExternalController>(new LoggerFactory());

        // Act
        var (user, provider, providerUserUserName, claims, errorMessage) = await AuthorizationHelpers.FindUserFromExternalProvider(authenticateResult, userManager, logger);

        // Assert
        user.ShouldNotBeNull();
    }

The issue with above. 上面的问题。

I am trying to moq a usermanager for my unit test 我正在尝试用户管理器进行单元测试

var exprectUser = new ApplicationUser()
        {
            UserName = "test@email.com",
            NormalizedUserName = "TEST@EMAIL.COM",
            NormalizedEmail = "TEST@EMAIL.COM",
            Email = "test@email.com",
            Id = 123,
            EmailConfirmed = true
        };

var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null,  null, null, null, null, null);

however when the method i am testing tries to find the user. 但是当我测试的方法试图找到用户时。

var findUser = await userManager.FindByEmailAsync("test@test.com");

it throws an error 它会抛出一个错误

Message: System.NotSupportedException : Store does not implement IUserEmailStore. 消息:System.NotSupportedException:Store未实现IUserEmailStore。

How do i implement IUserEmailStore in my moq usermanager? 如何在我的moq usermanager中实现IUserEmailStore?

My unit test project does contain the newest EntityFramework package. 我的单元测试项目确实包含最新的EntityFramework包。

Trying another way. 尝试另一种方式。

var founduser = userManager.Users.FirstOrDefault(e => e.Email.Equals("test@test.com", StringComparison.InvariantCultureIgnoreCase));

results in 结果是

System.NotSupportedException : Store does not implement IQueryableUserStore. System.NotSupportedException:Store未实现IQueryableUserStore。

I think i must be moqing this wrong. 我想我一定是莫青这个错了。

Update From comment 从评论更新

Ok i can moq the IUserEmailStore but I am not sure what i should do with it 好吧,我可以moq IUserEmailStore,但我不知道我应该怎么做

var mockEmailStore = new Mock<IUserEmailStore<ApplicationUser>>();

I managed to create a full moq usermanager that lets me search on email 我设法创建了一个完整的moq用户管理器,可以让我搜索电子邮件

 public class MoqUserManager : UserManager<ApplicationUser>
    {
        public MoqUserManager(IUserStore<ApplicationUser> userStore) : base(userStore,
                new Mock<IOptions<IdentityOptions>>().Object,
                new Mock<IPasswordHasher<ApplicationUser>>().Object,
                new IUserValidator<ApplicationUser>[0],
                new IPasswordValidator<ApplicationUser>[0],
                new Mock<ILookupNormalizer>().Object,
                new Mock<IdentityErrorDescriber>().Object,
                new Mock<IServiceProvider>().Object,
                new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
        { }

        public override Task<ApplicationUser> FindByEmailAsync(string email)
        {
            return Task.FromResult(new ApplicationUser { Email = email });
        }    
    }

which gives me 这给了我

var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
mockUserStore.Setup(x => x.FindByIdAsync(exprectUser.Id.ToString(), CancellationToken.None)).ReturnsAsync(exprectUser);
var userManager = new FakeUserManager(mockUserStore.Object);

So now i can verify that the proper user is returned from my identity server matching the federated login user. 所以现在我可以验证从匹配联合登录用户的身份服务器返回了正确的用户。

Okay your with the updated question the issue lies in 好的,问题在于更新的问题

var userManager = new UserManager<ApplicationUser>(mockUserStore.Object, null, null, null,  null, null, null, null, null);

This is not creating a mock, but an actual instance of UserManager<T> . 这不是创建模拟,而是创建UserManager<T>的实际实例。

You will have to do 你必须这样做

var userManagerMock = new Mock<UserManager<ApplicationUser>>(mockUserStore.Object, null, null, null,  null, null, null, null, null);

then do an setup 然后做一个设置

userManagerMock.Setup(um => um.FindByEmailAsync("test@email.com)).Returns(exprectUser)

and pass userManagerMock.Object to your 并将userManagerMock.Object传递给您的

var (user, provider, providerUserUserName, claims, errorMessage) = await AuthorizationHelpers.FindUserFromExternalProvider(authenticateResult, userManagerMock.Object, logger);

When mocking, you never want to call new on the external dependency and instead mock it, since then you can't change its behavior for a specific test. 在模拟时,你永远不想在外部依赖项上调用new ,而是模拟它,因为那样你就无法改变它对特定测试的行为。 UserManager<T> should have all or most public properties as virtual, so you can override them. UserManager<T>应将所有或大多数公共属性设置为虚拟属性,以便您可以覆盖它们。

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

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