Asp.net Core Identity unit test controller actions - c#

I'm having a problem working out how and what to test.
I have a controller that injects UserManager and calls the CreateAsync method to create a new user.
I don't want to test the Identity user manager as this has clearly been thoroughly tested already. What I would like to do is test that the controller runs through the correct paths (in my case, there are 3 paths, sending responses back with either model state errors, identity response errors or a simple string)
Should I be trying to create a mock of the user manager in order to create my test (I'm not sure how to set up user manager as a mock dependency)
Second, how can I set conditions to verify that the controller has taken a given path.
I am using xUnit and Moq.
[Route("api/[controller]")]
public class MembershipController : BaseApiController
{
private UserManager<ApplicationUser> _userManager;
public MembershipController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[HttpGet("RegisterNewUser")]
public HttpResponseMessage RegisterNewUser([FromBody] NewUserRegistration user)
{
if (ModelState.IsValid)
{
ApplicationUser newUser = new ApplicationUser();
newUser.UserName = user.username;
newUser.Email = user.password;
IdentityResult result = _userManager.CreateAsync(newUser, user.password).Result;
if (result.Errors.Count() > 0)
{
var errors = new IdentityResultErrorResponse().returnResponseErrors(result.Errors);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
}
else
{
var errors = new ViewModelResultErrorResponse().returnResponseErrors(ModelState);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
return this.WebApiResponse(
"We have sent a valifation email to you, please click on the verify email account link.",
HttpStatusCode.OK);
}
}
In My unit test I have the following to test a happy path scenario
[Fact]
public void RegisterNewUser_ReturnsHttpStatusOK_WhenValidModelPosted()
{
var mockStore = new Mock<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore.Object, null, null, null, null, null, null, null, null);
ApplicationUser testUser = new ApplicationUser { UserName = "user#test.com" };
mockStore.Setup(x => x.CreateAsync(testUser, It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(IdentityResult.Success));
mockStore.Setup(x => x.FindByNameAsync(testUser.UserName, It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(testUser));
mockUserManager.Setup(x => x.CreateAsync(testUser).Result).Returns(new IdentityResult());
MembershipController sut = new MembershipController(mockUserManager.Object);
var input = new NewUserInputBuilder().Build();
sut.RegisterNewUser(input);
}
Where "input" in sut.RegisterNewUser(input); refers to a helper class which constructs the viewmodel which the controller action requires:
public class NewUserInputBuilder
{
private string username { get; set; }
private string password { get; set; }
private string passwordConfirmation { get; set; }
private string firstname { get; set; }
private string lastname { get; set; }
internal NewUserInputBuilder()
{
this.username = "user#test.com";
this.password = "password";
this.passwordConfirmation = "password";
this.firstname = "user";
this.lastname = "name";
}
internal NewUserInputBuilder WithNoUsername()
{
this.username = "";
return this;
}
internal NewUserInputBuilder WithMisMatchedPasswordConfirmation()
{
this.passwordConfirmation = "MismatchedPassword";
return this;
}
internal NewUserRegistration Build()
{
return new NewUserRegistration
{ username = this.username, password = this.password,
passwordConfirmation = this.passwordConfirmation,
firstname = this.firstname, lastname = this.lastname
};
}
}
My aim here is to force 3 conditions via tests:
Create a valid viewmodel and return a success message
Create a valid viewmodel but returns a IdentityResponse error (eg. user exists) which gets converted to
Create an invalid viewmodel and returns Modelstate errors
The errors are handled using a abstract class which returns a json object
The base class for the controller simply constructs a HttpResponseMessage for return.
Basically I want to check that the correct error response class is called by forcing the test down the modelstate error path, the identityresult.errors path and that the happy path can be achieved.
Then my plan is to test the error response classes in isolation.
Hopefully that is enough detail.

Mehod under test should be made async and not use blocking calls ie .Result
[HttpGet("RegisterNewUser")]
public async Task<HttpResponseMessage> RegisterNewUser([FromBody] NewUserRegistration user) {
if (ModelState.IsValid) {
var newUser = new ApplicationUser() {
UserName = user.username,
Email = user.password
};
var result = await _userManager.CreateAsync(newUser, user.password);
if (result.Errors.Count() > 0) {
var errors = new IdentityResultErrorResponse().returnResponseErrors(result.Errors);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
} else {
var errors = new ViewModelResultErrorResponse().returnResponseErrors(ModelState);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
return this.WebApiResponse(
"We have sent a valifation email to you, please click on the verify email account link.",
HttpStatusCode.OK);
}
Review of Happy path scenario and method under test shows that there is no need to setup the UserStore as test will be overriding the user manager virtual members directly.
Note the test has also been made async as well.
Create a valid viewmodel and return a success message
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusOK_WhenValidModelPosted() {
//Arrange
var mockStore = Mock.Of<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore, null, null, null, null, null, null, null, null);
mockUserManager
.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
.ReturnsAsync(IdentityResult.Success);
var sut = new MembershipController(mockUserManager.Object);
var input = new NewUserInputBuilder().Build();
//Act
var actual = await sut.RegisterNewUser(input);
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.IsSuccessStatusCode == true);
}
Create a valid viewmodel but returns a IdentityResponse error (eg. user exists) which gets converted
For this you just need to setup the mock to return a result with errors.
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusBadRequest_WhenViewModelPosted() {
//Arrange
//...code removed for brevity
mockUserManager
.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
.ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "test"}));
//...code removed for brevity
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.StatusCode == HttpStatusCode.BadRequest);
}
And for
Create an invalid viewmodel and returns Modelstate errors
You just need to set the model state of the controller so that it is invalid.
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusBadRequest_WhenInvalidModelState() {
//Arrange
var mockStore = Mock.Of<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore, null, null, null, null, null, null, null, null);
var sut = new MembershipController(mockUserManager.Object);
sut.ModelState.AddModelError("", "invalid data");
var input = new NewUserInputBuilder().Build();
//Act
var actual = await sut.RegisterNewUser(input);
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.StatusCode == HttpStatusCode.BadRequest);
}
FluentAssertions were used to do all the assertions. You could just as easily used Assert.* API.
This should be enough to get you on your way with the above question.

Here's a simple way using NUnit (you could do something similar with xUnit), if you don't want to test the user manager. (I've also shown how the DbContext can be passed to the same controller, using an in-memory database that can be used for setting up mock data)
private DbContextOptions<MyContextName> options;
[OneTimeSetUp]
public void SetUp()
{
options = new DbContextOptionsBuilder<MyContextName>()
.UseInMemoryDatabase(databaseName: "MyDatabase")
.Options;
// Insert seed data into the in-memory mock database using one instance of the context
using (var context = new MyContextName(options))
{
var testWibble = new Wibble { MyProperty = 1, MyOtherProperty = 2 ... };
context.wibbles.Add(testWibble);
context.SaveChanges();
}
}
[Test]
public void Some_TestMethod()
{
// Use a clean instance of the context to run the test
using (var context = new MyDbContext(options))
{
var store = new UserStore<MyUserType>(context);
var userManager = new UserManager<MyUserType>(store, null, null, null, null, null, null, null, null);
MyController MyController = new MyController(userManager, context);
... test the controller
}
}

Related

.Net Core Unit Testing

I have a web api as below, which is being called from my angularjs UI.
public class ValuesController : Controller
{
private static string dynamoDbTable = string.Empty;
private readonly IDynamoDbManager<MyModel> _dynamoDbManager;
public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbManager<MyModel> dynamoDbManager)
{
var vals = appSettings.Value;
dynamoDbTable = vals["dynamoDbTable"];
_dynamoDbManager = dynamoDbManager;
}
[HttpGet("api/data")]
public async Task<IActionResult> GetAllData(string status, string role)
{
List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("status", ScanOperator.Contains, status));
conditions.Add(new ScanCondition("role", ScanOperator.Contains, role));
List<MyModel> model = new List<MyModel>();
model = await _dynamoDbManager.GetAsync(conditions);
return Ok(model);
}
[HttpPost("api/save")]
public async Task<IActionResult> SaveData([FromBody] List<MyModel> listData, string input, string name, string type)
{
List<MyModel> model = new List<MyModel>();
foreach (var data in listData)
{
//populating data here and saving
await _dynamoDbManager.SaveAsync(data);
}
return Ok();
}
}
}
Now I want to write test cases for my API endpoints. I have not written any unit test case before so need input in that. I have read basic documentation of xunit here.
Here is my sample test method:
public class ValuesControllerTests
{
private Mock<IDynamoDbManager<MyModel>> _dbManager;
private ValuesController _valueController;
public ValuesControllerTests()
{
var mockRepository = new MockRepository(MockBehavior.Loose);
_dbManager = mockRepository.Create<IDynamoDbManager<MyModel>>();
var options = new OptionsWrapper<Dictionary<string, string>>(new Dictionary<string, string>()
{
{"dynamoDbTable", nameof(MyModel) }
});
_valueController = new ValuesController(options, _dbManager.Object);
}
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ }
};
//Here I am trying to use MOQ but this is not working fine
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var result = _valueController.GetAllData("new", "admin");
Assert.IsType<OkObjectResult>(result.Result);
}
}
}
Can I test my method without using moq, if so how can I do so? Because with moq I am not able to test.
Secondly above test case I am just checking if api method returns OK. What other test cases can I write for my get and save method.
I have also created a git project in case anyone wants to look at project: https://github.com/kj1981/xunit
Would appreciate if someone can help me in this.
--Added--
I also tried one test as below, but is this fine? May be my thinking is wrong, with the below test I thought it would fail but it does not.
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ Id ="1", Name = "Adam", Role = "User", Status ="old" },
new MyModel(){ Id ="2", Name = "Gary", Role = "Admin", Status ="new" }
};
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var okResult = _valueController.GetAllData("fail", "none").Result as OkObjectResult; ;
var items = Assert.IsType<List<MyModel>>(okResult.Value);
Assert.Equal(2, items.Count);
}
Anyone with inputs ?

Unit Testing Password Validators in ASP.NET Core [duplicate]

I have a CustomPasswordValidator.cs file that overrides PasswordValidator
public class CustomPasswordValidator : PasswordValidator<AppUser>
{ //override the PasswordValidator functionality with the custom definitions
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
IdentityResult result = await base.ValidateAsync(manager, user, password);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
//check that the username is not in the password
if (password.ToLower().Contains(user.UserName.ToLower()))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsUserName",
Description = "Password cannot contain username"
});
}
//check that the password doesn't contain '12345'
if (password.Contains("12345"))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsSequence",
Description = "Password cannot contain numeric sequence"
});
}
//return Task.FromResult(errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
I am new to using Moq and xUnit. I am trying to create a unit test to ensure that the correct number of errors are produced (showing working code, with code that produced errors in comments):
//test the ability to validate new passwords with Infrastructure/CustomPasswordValidator.cs
[Fact]
public async void Validate_Password()
{
//Arrange
<Mock><UserManager<AppUser>> userManager = new <Mock><UserManager<AppUser>>(); //caused null exception, use GetMockUserManager() instead
<Mock><CustomPasswordValidator> customVal = new <Mock><CustomPasswordValidator>(); //caused null result object use customVal = new <CustomPasswordValidator>() instead
<AppUser> user = new <AppUser>
user.Name = "user"
//set the test password to get flagged by the custom validator
string testPwd = "Thi$user12345";
//Act
//try to validate the user password
IdentityResult result = await customVal.ValidateAsync(userManager, user, testPwd);
//Assert
//demonstrate that there are two errors present
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
Assert.Equal(errors.Count, 2);
}
//create a mock UserManager class
private Mock<UserManager<AppUser>> GetMockUserManager()
{
var userStoreMock = new Mock<IUserStore<AppUser>>();
return new Mock<UserManager<AppUser>>(
userStoreMock.Object, null, null, null, null, null, null, null, null);
}
The error occurs on the IdentityResult line, indicating that I cannot convert a Mock to UserManager and cannot convert Mock to AppUser class.
EDIT: changed to include GetMockUserManager() needed to mock a UserManagerClass in ASP.NET core (Mocking new Microsoft Entity Framework Identity UserManager and RoleManager)
With Moq you need call .Object on the mock to get the mocked object. You should also make the test async and await the method under test.
You are also mocking the subject under test which in this case is cause the method under test to return null when called because it would not have been setup appropriately. You are basically testing the mocking framework at that point.
Create an actual instance of the subject under test CustomPasswordValidator and exercise the test, mocking the explicit dependencies of the subject under test to get the desired behavior.
public async Task Validate_Password() {
//Arrange
var userManagerMock = new GetMockUserManager();
var subjetUnderTest = new CustomPasswordValidator();
var user = new AppUser() {
Name = "user"
};
//set the test password to get flagged by the custom validator
var password = "Thi$user12345";
//Act
IdentityResult result = await subjetUnderTest.ValidateAsync(userManagerMock.Object, user, password);
//...code removed for brevity
}
Read Moq Quickstart to get more familiar with how to use moq.

How to mock UserManager in .Net Core testing?

I have following code. Im trying to running a test case for create user.Following is what i have tried so far.
public class CreateUserCommandHandlerTest
{
private Mock<UserManager<ApplicationUser>> _userManager;
private CreateUserCommandHandler _systemUnderTest;
public CreateUserCommandHandlerTest()
{
_userManager = MockUserManager.GetUserManager<ApplicationUser>();
var user = new ApplicationUser() { UserName = "ancon1", Email = "ancon#mail.com", RoleType = RoleTypes.Anonymous };
_userManager
.Setup(u => u.CreateAsync(user, "ancon2")).ReturnsAsync(IdentityResult.Success);
_systemUnderTest = new CreateUserCommandHandler(_userManager.Object);
}
[Fact]
public async void Handle_GivenValidInput_ReturnsCreatedResponse()
{
var command = new CreateUserCommand { Username = "ancon1", Email = "ancon#mail.com", Password = "ancon2", RoleType = RoleTypes.Anonymous };
var result = await _systemUnderTest.Handle(command, default(CancellationToken));
Assert.NotNull(result);
Assert.IsType<Application.Commands.CreatedResponse>(result);
}
}
My User manager is here:
public static class MockUserManager
{
public static Mock<UserManager<TUser>> GetUserManager<TUser>()
where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var passwordHasher = new Mock<IPasswordHasher<TUser>>();
IList<IUserValidator<TUser>> userValidators = new List<IUserValidator<TUser>>
{
new UserValidator<TUser>()
};
IList<IPasswordValidator<TUser>> passwordValidators = new List<IPasswordValidator<TUser>>
{
new PasswordValidator<TUser>()
};
userValidators.Add(new UserValidator<TUser>());
passwordValidators.Add(new PasswordValidator<TUser>());
var userManager = new Mock<UserManager<TUser>>(store.Object, null, passwordHasher.Object, userValidators, passwordValidators, null, null, null, null);
return userManager;
}
}
and my Command handler is this:
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, BaseCommandResponse>
{
private readonly UserManager<ApplicationUser> _userManager;
public CreateUserCommandHandler(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<BaseCommandResponse> Handle(CreateUserCommand createUserCommand, CancellationToken cancellationToken)
{
var user = new ApplicationUser { UserName = createUserCommand.Username, Email = createUserCommand.Email, RoleType = createUserCommand.RoleType };
var result = await _userManager.CreateAsync(user, createUserCommand.Password);
if (result.Succeeded)
{
return new CreatedResponse();
}
ErrorResponse errorResponse = new ErrorResponse(result.Errors.Select(e => e.Description).First());
return errorResponse;
}
}
when i'm running my test it fails and saying Object reference not set to an instant of an object.
What am i doing wrong here??
I know this is months old but I keep getting back to this thread. I will extend my own answer on this topic because just pointing to Haok's GitHub example is like saying: "Read a book" as it is huge. It does not pinpoint the issue and what you need to do. You need to isolate a Mock object, but not only that but also you need to 'Setup' the method for 'CreateAsync'. So let's put this in three parts:
You need to MOCK if you are using MOQ or a similar framework to make a mocked up creation of the UserManager.
You need to Setup the methods of UserManager you expect to get results back from.
Optionally you would want to inject some generic list from a mocked Entity Framework Core 2.1 or similar so that you can actually see that a list of IDentity Users actually increases or decreases. Not just that UserManager succeeded and nothing else
So say I have a helper method for returning a Mocked UserManager. Which is just slightly altered from the Haok code:
public static Mock<UserManager<TUser>> MockUserManager<TUser>(List<TUser> ls) where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null);
mgr.Object.UserValidators.Add(new UserValidator<TUser>());
mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());
mgr.Setup(x => x.DeleteAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);
mgr.Setup(x => x.CreateAsync(It.IsAny<TUser>(), It.IsAny<string>())).ReturnsAsync(IdentityResult.Success).Callback<TUser, string>((x, y) => ls.Add(x));
mgr.Setup(x => x.UpdateAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);
return mgr;
}
What is key to this is I am injecting a generic 'TUser' that is what I will be testing as well injecting a list of this. Similar to my example of:
private List<ApplicationUser> _users = new List<ApplicationUser>
{
new ApplicationUser("User1", "user1#bv.com") { Id = 1 },
new ApplicationUser("User2", "user2#bv.com") { Id = 2 }
};
...
private _userManager = MockUserManager<ApplicationUser>(_users).Object;
Then finally I am testing a pattern with a repository similar to this implementation I want to test:
public async Task<int> CreateUser(ApplicationUser user, string password) => (await _userManager.CreateAsync(user, password)).Succeeded ? user.Id : -1;
I test it like this:
[Fact]
public async Task CreateAUser()
{
var newUser = new ApplicationUser("NewUser", "New#test.com");
var password = "P#ssw0rd!";
var result = await CreateUser(newUser, password);
Assert.Equal(3, _users.Count);
}
The key to what I did is that not only did I 'Setup' the CreateAsync but I provided a callback so I can actually see my list I inject get incremented. Hope this helps someone.
aspnet/Identity is opensource so what you can do is see how they mock it themselves.
Here's how they do it: MockHelpers.cs
TestUserManager
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
{
store = store ?? new Mock<IUserStore<TUser>>().Object;
var options = new Mock<IOptions<IdentityOptions>>();
var idOptions = new IdentityOptions();
idOptions.Lockout.AllowedForNewUsers = false;
options.Setup(o => o.Value).Returns(idOptions);
var userValidators = new List<IUserValidator<TUser>>();
var validator = new Mock<IUserValidator<TUser>>();
userValidators.Add(validator.Object);
var pwdValidators = new List<PasswordValidator<TUser>>();
pwdValidators.Add(new PasswordValidator<TUser>());
var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(), null,
new Mock<ILogger<UserManager<TUser>>>().Object);
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>()))
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
return userManager;
}
In .NetCore 2.2 you have to do it slightly different. Treat it like an update to #Nick Chapsas answer.
First of all, you have to use IUserPasswordStore instead of IUserStore. IUserPasswordStore inherits IUserStore, but UserManager would like to get IUserPasswordStore. In other way, some things won't work.
If you want to test real behaviour of UserManager (for example CreateUserAsync), you can use real implementations of UserValidator and PasswordValidator. You may want to just to be sure that your method reacts how it supposed to for CreateUser errors.
This is my updated example:
UserManager<TUser> CreateUserManager() where TUser : class
{
Mock<IUserPasswordStore<TUser>> userPasswordStore = new Mock<IUserPasswordStore<TUser>>();
userPasswordStore.Setup(s => s.CreateAsync(It.IsAny<TUser>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(IdentityResult.Success));
var options = new Mock<IOptions<IdentityOptions>>();
var idOptions = new IdentityOptions();
//this should be keep in sync with settings in ConfigureIdentity in WebApi -> Startup.cs
idOptions.Lockout.AllowedForNewUsers = false;
idOptions.Password.RequireDigit = true;
idOptions.Password.RequireLowercase = true;
idOptions.Password.RequireNonAlphanumeric = true;
idOptions.Password.RequireUppercase = true;
idOptions.Password.RequiredLength = 8;
idOptions.Password.RequiredUniqueChars = 1;
idOptions.SignIn.RequireConfirmedEmail = false;
// Lockout settings.
idOptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
idOptions.Lockout.MaxFailedAccessAttempts = 5;
idOptions.Lockout.AllowedForNewUsers = true;
options.Setup(o => o.Value).Returns(idOptions);
var userValidators = new List<IUserValidator<TUser>>();
UserValidator<TUser> validator = new UserValidator<TUser>();
userValidators.Add(validator);
var passValidator = new PasswordValidator<TUser>();
var pwdValidators = new List<IPasswordValidator<TUser>>();
pwdValidators.Add(passValidator);
var userManager = new UserManager<TUser>(userPasswordStore.Object, options.Object, new PasswordHasher<TUser>(),
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(), null,
new Mock<ILogger<UserManager<TUser>>>().Object);
return userManager;
}
Notice that UserPasswordStore has a method (CreateAsync) that should be mocked if you want to test CreateAsync from UserManager.
Password and Lockout settings are taken from my project. They should be kept in sync with your settings, so that you can test the real thing.
Of course you don't test for example PasswordValidator, but you can test your methods, for example:
//Part of user service
public async Task<IdentityResult> Register(UserDto data)
{
SystemUser user = ConvertDtoToUser(data);
IdentityResult result = userManager.CreateAsync(user, data.Password);
//some more code that is dependent on the result
}

Unit Testing Custom Password Validators in ASP.NET Core

I have a CustomPasswordValidator.cs file that overrides PasswordValidator
public class CustomPasswordValidator : PasswordValidator<AppUser>
{ //override the PasswordValidator functionality with the custom definitions
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
IdentityResult result = await base.ValidateAsync(manager, user, password);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
//check that the username is not in the password
if (password.ToLower().Contains(user.UserName.ToLower()))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsUserName",
Description = "Password cannot contain username"
});
}
//check that the password doesn't contain '12345'
if (password.Contains("12345"))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsSequence",
Description = "Password cannot contain numeric sequence"
});
}
//return Task.FromResult(errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
I am new to using Moq and xUnit. I am trying to create a unit test to ensure that the correct number of errors are produced (showing working code, with code that produced errors in comments):
//test the ability to validate new passwords with Infrastructure/CustomPasswordValidator.cs
[Fact]
public async void Validate_Password()
{
//Arrange
<Mock><UserManager<AppUser>> userManager = new <Mock><UserManager<AppUser>>(); //caused null exception, use GetMockUserManager() instead
<Mock><CustomPasswordValidator> customVal = new <Mock><CustomPasswordValidator>(); //caused null result object use customVal = new <CustomPasswordValidator>() instead
<AppUser> user = new <AppUser>
user.Name = "user"
//set the test password to get flagged by the custom validator
string testPwd = "Thi$user12345";
//Act
//try to validate the user password
IdentityResult result = await customVal.ValidateAsync(userManager, user, testPwd);
//Assert
//demonstrate that there are two errors present
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
Assert.Equal(errors.Count, 2);
}
//create a mock UserManager class
private Mock<UserManager<AppUser>> GetMockUserManager()
{
var userStoreMock = new Mock<IUserStore<AppUser>>();
return new Mock<UserManager<AppUser>>(
userStoreMock.Object, null, null, null, null, null, null, null, null);
}
The error occurs on the IdentityResult line, indicating that I cannot convert a Mock to UserManager and cannot convert Mock to AppUser class.
EDIT: changed to include GetMockUserManager() needed to mock a UserManagerClass in ASP.NET core (Mocking new Microsoft Entity Framework Identity UserManager and RoleManager)
With Moq you need call .Object on the mock to get the mocked object. You should also make the test async and await the method under test.
You are also mocking the subject under test which in this case is cause the method under test to return null when called because it would not have been setup appropriately. You are basically testing the mocking framework at that point.
Create an actual instance of the subject under test CustomPasswordValidator and exercise the test, mocking the explicit dependencies of the subject under test to get the desired behavior.
public async Task Validate_Password() {
//Arrange
var userManagerMock = new GetMockUserManager();
var subjetUnderTest = new CustomPasswordValidator();
var user = new AppUser() {
Name = "user"
};
//set the test password to get flagged by the custom validator
var password = "Thi$user12345";
//Act
IdentityResult result = await subjetUnderTest.ValidateAsync(userManagerMock.Object, user, password);
//...code removed for brevity
}
Read Moq Quickstart to get more familiar with how to use moq.

Mocked repository returning object with null properties

Here's a sample of one of my unit test classes (pared down to the basics). In the controller, when the Index() action method is invoked, a call to GetByID(1234) always results in a newed up instance of a Ticket object. The object exists, but all of its properties are null, even though I've set them in my fake object. Any ideas as to why?
I'm using Moq.
Unit test
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>();
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
Mock<HttpContextBase> context = FakeHttpContext();
context.Setup(ctx => ctx.Session[SessionKeys.TokenData.ToString()]).Returns(Constants.TOKENDATA_SUBMITTER);
MockTicketRepository.Setup(x => x.GetById(It.IsAny<int>())).Returns(Constants.CLIENT_TICKET);
//ACT
var result = GetController(context.Object).Index(Constants.TICKET_ID);
var model = ((ViewResult)result).Model;
//ASSERT
Assert.IsInstanceOfType(model, typeof(TicketModel), "ViewModel should have been an instance of TicketModel.");
}
private TicketController GetController(HttpContextBase context)
{
var controller = new TicketController(MockTicketRepository.Object);
controller.ControllerContext = GetControllerContext(context, controller);
return controller;
}
}
Constants.CLIENT_TICKET
public static Ticket CLIENT_TICKET
{
get
{
var ticket = new Ticket
{
CategoryID = 1,
CreatedByUserId = 4
};
ticket.Clients.Add(new Client { ShortName = "Test Client 1"});
ticket.Clients.Add(new Client { ShortName = "Test Client 2" });
ticket.User = new User {FirstName = "First", LastName = "Last"};
return ticket;
}
}
Controller
private readonly ITicketRepository _ticketRepository;
public TicketController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ActionResult Index(int id)
{
var ticket = _ticketRepository.GetById(id);
// etc...
}
Could you show the controller code under test? It could be related to how you have set up the mocked context but it's hard to tell without seeing the controller code.
Also, if you add MockBehavior.Strict when you create the mock, it will bomb out if the invocation doesn't have a corresponding expectation:
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
UPDATE
I've tried to strip everything back so that the test is as simple as possible to try and isolate the issue. Here's what I have come up with:
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository;
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
MockTicketRepository.Setup(x => x.GetById(Constants.TICKET_ID)).Returns(Constants.CLIENT_TICKET);
var controller = new TicketController(MockTicketRepository.Object);
//ACT - try to keep ACT as lean as possible, ideally just the method call you're testing
var result = controller.Index(Constants.TICKET_ID);
//ASSERT
var model = ((ViewResult)result).ViewData.Model;
Assert.That(model, Is.InstanceOfType<TicketModel>(), "ViewModel should have been an instance of TicketModel.")
}
}

Categories

Resources