New to unit testing with Moq and xUnit. I am trying to mock a SignInManager
that is used in a controller constructor to build a unit test. The documentation that I can find for the SignInManager
constructor says it accepts a UserManager
and AuthenticationManager
object: https://msdn.microsoft.com/en-us/library/mt173769(v=vs.108).aspx#M:Microsoft.AspNet.Identity.Owin.SignInManager`2.
When I try to mock the controller, I get an error saying it was unable to instantiate a proxy of the SignInManager
and AuthenticationManager
classes.
Error:
"Message: Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Microsoft.AspNetCore.Identity.SignInManager1[[Models.AppUser, , Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Could not find a constructor that would match given arguments: Castle.Proxies.UserManager`1Proxy Castle.Proxies.AuthenticationManagerProxy"
The unit test:
public void Can_Send_Password_Reset_Email()
{
//Arrange
//create mock services
Mock<IEmailService> mockEmailService = new Mock<IEmailService>();
Mock<ILessonRepository> mockRepo = new Mock<ILessonRepository>();
Mock<UserManager<AppUser>> mockUsrMgr = GetMockUserManager();
var mockSignInMgr = GetMockSignInManager();
Mock<UserValidator<AppUser>> mockUsrVal = new Mock<UserValidator<AppUser>>();
Mock<PasswordValidator<AppUser>> mockPwdVal = new Mock<PasswordValidator<AppUser>>();
Mock<PasswordHasher<AppUser>> mockPwdHshr = new Mock<PasswordHasher<AppUser>>();
Mock<ForgotPasswordModel> model = new Mock<ForgotPasswordModel>();
model.Object.Email = "[email protected]";
var user = new AppUser();
var token = mockUsrMgr.Object.GeneratePasswordResetTokenAsync(user).Result;
//create mock temporary data, needed for controller message
Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>();
//create the controller
//ERROR ON THIS LINE
AccountController controller = new AccountController(mockUsrMgr.Object, mockSignInMgr.Object, mockUsrVal.Object, mockPwdVal.Object, mockPwdHshr.Object, mockEmailService.Object)
{
TempData = tempData.Object
};
//Act
//the controller should call the email action method
controller.PasswordResetEmail(model.Object);
//Assert
//verify that the email service method was called one time
mockEmailService.Verify(m => m.PasswordResetMessage(user, "Test Email"), Times.Once());
}
The SignInManager mocking function:
//create a mock SignInManager class
private Mock<SignInManager<AppUser>> GetMockSignInManager()
{
var mockUsrMgr = GetMockUserManager();
var mockAuthMgr = new Mock<AuthenticationManager>();
return new Mock<SignInManager<AppUser>>(mockUsrMgr.Object, mockAuthMgr.Object);
}
The GetMockUserManager()
works fine in other unit tests and doesn't appear to be the issue.
This is how I do it, I hope this helps you.
public class FakeSignInManager : SignInManager<ApplicationUser>
{
public FakeSignInManager()
: base(new FakeUserManager(),
new Mock<IHttpContextAccessor>().Object,
new Mock<IUserClaimsPrincipalFactory<ApplicationUser>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<ILogger<SignInManager<ApplicationUser>>>().Object,
new Mock<IAuthenticationSchemeProvider>().Object)
{ }
}
public class FakeUserManager : UserManager<ApplicationUser>
{
public FakeUserManager()
: base(new Mock<IUserStore<ApplicationUser>>().Object,
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<IdentityResult> CreateAsync(ApplicationUser user, string password)
{
return Task.FromResult(IdentityResult.Success);
}
public override Task<IdentityResult> AddToRoleAsync(ApplicationUser user, string role)
{
return Task.FromResult(IdentityResult.Success);
}
public override Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
{
return Task.FromResult(Guid.NewGuid().ToString());
}
}
For ASP.NET Core 5, this setup can be used:
var userManagerMock = new Mock<UserManager<User>>(
/* IUserStore<TUser> store */Mock.Of<IUserStore<User>>(),
/* IOptions<IdentityOptions> optionsAccessor */null,
/* IPasswordHasher<TUser> passwordHasher */null,
/* IEnumerable<IUserValidator<TUser>> userValidators */null,
/* IEnumerable<IPasswordValidator<TUser>> passwordValidators */null,
/* ILookupNormalizer keyNormalizer */null,
/* IdentityErrorDescriber errors */null,
/* IServiceProvider services */null,
/* ILogger<UserManager<TUser>> logger */null);
var signInManagerMock = new Mock<SignInManager<User>>(
userManagerMock.Object,
/* IHttpContextAccessor contextAccessor */Mock.Of<IHttpContextAccessor>(),
/* IUserClaimsPrincipalFactory<TUser> claimsFactory */Mock.Of<IUserClaimsPrincipalFactory<User>>(),
/* IOptions<IdentityOptions> optionsAccessor */null,
/* ILogger<SignInManager<TUser>> logger */null,
/* IAuthenticationSchemeProvider schemes */null,
/* IUserConfirmation<TUser> confirmation */null);
the SignInManager
Has no Constructor with no argument so you have to pass the arguments in the new Mock<SignInManager>(**Params here**)
Some of these paramaters cannot not be null so they have to be mocked too like :
UserManager<TUser>
==> the First Argument.IHttpContextAccessor
==> the second argument.The probleme here is even UserManager
have not a parameter less constructor so you have to mock with its parameters
For Usermanager
the parameter that should not be null IUserStore<User>
==> the first parameter
so here how I did with minimum code
var _mockUserManager = new Mock<ApiUserManager>(new Mock<IUserStore<ApiUser>>().Object,
null, null, null, null, null, null, null, null);
var _contextAccessor = new Mock<IHttpContextAccessor>();
var _userPrincipalFactory = new Mock<IUserClaimsPrincipalFactory<ApiUser>>();
Mock<ApiSignInManager>mockApiSignInManager = new Mock<ApiSignInManager>(_mockUserManager.Object,
_contextAccessor.Object, _userPrincipalFactory.Object, null, null, null);
this response were inspired by umutesen on GitHub Forum
The documentation you are looking at is for Microsoft.AspNet.Identity.Owin.SignInManager
.
If you look at the error message, you will see that Moq is actually trying to create an object of type Microsoft.AspNetCore.Identity.SignInManager
. Note the difference beetween AspNet
and AspNetCore
.
So basically, you were looking at the SignInManager
that was used in the old Owin-based Identity stack for the classic ASP.NET. But you need to look at the ASP.NET Core version. If you look at the documentation of that type, you will see that the constructor has a few more dependencies:
public SignInManager(
UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<TUser> claimsFactory,
IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
ILogger<Microsoft.AspNetCore.Identity.SignInManager<TUser>> logger,
IAuthenticationSchemeProvider schemes);
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