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.
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);
}
[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", "[email protected]"),
},
"FakeScheme"));
var authenticateResult = AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties() { Items = { { "scheme", "fed" } } }, "FakeScheme"));
var exprectUser = new ApplicationUser()
{
UserName = "[email protected]",
NormalizedUserName = "[email protected]",
NormalizedEmail = "[email protected]",
Email = "[email protected]",
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();
}
I am trying to moq a usermanager for my unit test
var exprectUser = new ApplicationUser()
{
UserName = "[email protected]",
NormalizedUserName = "[email protected]",
NormalizedEmail = "[email protected]",
Email = "[email protected]",
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("[email protected]");
it throws an error
Message: System.NotSupportedException : Store does not implement IUserEmailStore.
How do i implement IUserEmailStore in my moq usermanager?
My unit test project does contain the newest EntityFramework package.
Trying another way.
var founduser = userManager.Users.FirstOrDefault(e => e.Email.Equals("[email protected]", StringComparison.InvariantCultureIgnoreCase));
results in
System.NotSupportedException : Store does not implement IQueryableUserStore.
I think i must be moqing this wrong.
Ok i can moq the IUserEmailStore but I am not sure what i should do with it
var mockEmailStore = new Mock<IUserEmailStore<ApplicationUser>>();
I managed to create a full moq usermanager that lets me search on email
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>
.
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("[email protected])).Returns(exprectUser)
and pass userManagerMock.Object
to your
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. UserManager<T>
should have all or most public properties as virtual, so you can override them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With