In this article, we will learn how to perform Unit Testing with UserManager and RoleManager in ASP.NET Core Identity. Unit testing is an essential practice in software development that helps ensure the correctness and reliability of code. In the context of ASP.NET Core Identity, unit testing becomes even more critical, as it involves sensitive user-related operations such as registration and authentication.

We use the UserManager class to perform user-related operations, such as user registration and authentication. Moreover, we use the RoleManager class to manage and access user roles in our application. During unit testing, we need a way to mock those objects, to test the functionality of our controllers in isolation.

Here, we will test the user registration process that makes use of both objects. The code is based on the article on user registration that is part of the ASP.NET Core Identity series.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

By the end of this article, you will have a comprehensive understanding of how to unit test the user registration process in ASP.NET Core Identity and improve the quality and reliability of your code.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

The User Registration Process

During registration, the user has to enter his details: first and last name, username, and password. Additionally, the user has to select the appropriate role (Visitor or Administrator).

The registration process is handled by the AccountController class:

public class AccountController : Controller
{
    private readonly IMapper _mapper;
    private readonly UserManager<User> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;

    public AccountController(IMapper mapper, 
        UserManager<User> userManager, RoleManager<IdentityRole> roleManager)
    {
        _mapper = mapper;
        _userManager = userManager;
        _roleManager = roleManager;
    }
    //other methods omitted
}

The Register() method in the Account controller handles the registration POST request and uses UserManager and RoleManager to create a new user and assign them a role:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(UserRegistrationModel userModel)
{
    ViewData["roles"] = _roleManager.Roles.ToList();

    if (!ModelState.IsValid)
    {
        return View(userModel);
    }

    var user = _mapper.Map<User>(userModel);

    var result = await _userManager.CreateAsync(user, userModel.Password);
    if(!result.Succeeded)
    {
        foreach (var error in result.Errors)
        {
            ModelState.TryAddModelError(error.Code, error.Description);
        }

        return View(userModel);
    }

    await _userManager.AddToRoleAsync(user, userModel.Role);

    return RedirectToAction(nameof(HomeController.Index), "Home");
}

Let’s see how to define those roles in the application.

Initially, let’s create a new class (RoleConfiguration) that implements the IEntityTypeConfiguration<IdentityRole> interface:

public class RoleConfiguration : IEntityTypeConfiguration<IdentityRole>
{
    public void Configure(EntityTypeBuilder<IdentityRole> builder)
    {
        builder.HasData(
            new IdentityRole
            {
                Name = "Visitor",
                NormalizedName = "VISITOR"
            },
            new IdentityRole
            {
                Name = "Administrator",
                NormalizedName = "ADMINISTRATOR"
            });
    }
}

RoleConfiguration creates two new IdentityRole objects, named Visitor and Administrator respectively.

Next, let’s override the OnModelCreating() method in the database context file (ApplicationContext.cs), to apply the new configuration:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.ApplyConfiguration(new RoleConfiguration());
}

Now, the new roles are available to the Account controller and are displayed with a drop-down box in the respective view. Next, let’s proceed with unit testing of this controller.

Unit Testing With UserManager and RoleManager

In order to unit test the registration controller in isolation, we will need to mock the UserManager and RoleManager objects. Mocking helps us simulate the external dependencies of the class under test. We will use Moq, a mock object framework for .NET that helps us create mock objects for unit testing.

First of all, let’s create a new xUnit test project and let’s add support for Moq. We will begin by testing a successful registration to ensure that the controller creates a new user and assigns them a role correctly.

Successful Registration Scenario

First, let’s define the user registration model and the user object that would be created by the object mapper:

var userRegistrationModel = new UserRegistrationModel()
{
    FirstName = "Test1",
    LastName = "Test2",
    Email = "Test3",
    Password = "Test4",
    ConfirmPassword = "Test4"
};

var user = new User()
{
    UserName = "Test3",
    FirstName = "Test1",
    LastName = "Test2",
    Email = "Test3"
};

Next, let’s mock the UserManager object:

var userManagerMock = new Mock<UserManager<User>>(
    new Mock<IUserStore<User>>().Object,
    new Mock<IOptions<IdentityOptions>>().Object,
    new Mock<IPasswordHasher<User>>().Object,
    new IUserValidator<User>[0],
    new IPasswordValidator<User>[0],
    new Mock<ILookupNormalizer>().Object,
    new Mock<IdentityErrorDescriber>().Object,
    new Mock<IServiceProvider>().Object,
    new Mock<ILogger<UserManager<User>>>().Object);

userManagerMock
    .Setup(userManager => userManager.CreateAsync(It.IsAny<User>(), It.IsAny<string>()))
    .Returns(Task.FromResult(IdentityResult.Success));
userManagerMock
    .Setup(userManager => userManager.AddToRoleAsync(It.IsAny<User>(), It.IsAny<string>()));

For the UserManager mock object, we are mocking the two methods (CreateAsync() and AddToRoleAsync()).

Now, let’s mock the RoleManager object:

var list = new List<IdentityRole>()
{
    new IdentityRole("Administrator"),
    new IdentityRole("Visitor")
}
.AsQueryable();

var roleManagerMock = new Mock<RoleManager<IdentityRole>>(
    new Mock<IRoleStore<IdentityRole>>().Object,
    new IRoleValidator<IdentityRole>[0],
    new Mock<ILookupNormalizer>().Object,
    new Mock<IdentityErrorDescriber>().Object,
    new Mock<ILogger<RoleManager<IdentityRole>>>().Object);

roleManagerMock
    .Setup(r => r.Roles).Returns(list);

The RoleManager mock object will return a list of IdentityRole objects when we access the Roles property.

Finally, let’s also mock the Mapper object and use it, along with the other two mock objects, to instantiate the AccountController object:

var mapperMock = new Mock<IMapper>();
mapperMock.Setup(m => m.Map<User>(userRegistrationModel)).Returns(user);

var controller = new AccountController(mapperMock.Object, userManagerMock.Object, roleManagerMock.Object);
var result = (RedirectToActionResult) await controller.Register(userRegistrationModel);

Assert.Equal("Index", result.ActionName);

Failed Registration Scenario

We will also test a scenario where the registration fails due to an existing username:

var userManagerMock = new Mock<UserManager<User>>(
    new Mock<IUserStore<User>>().Object,
    new Mock<IOptions<IdentityOptions>>().Object,
    new Mock<IPasswordHasher<User>>().Object,
    new IUserValidator<User>[0],
    new IPasswordValidator<User>[0],
    new Mock<ILookupNormalizer>().Object,
    new Mock<IdentityErrorDescriber>().Object,
    new Mock<IServiceProvider>().Object,
    new Mock<ILogger<UserManager<User>>>().Object);

var identityErrors = new IdentityError[]
{
    new IdentityError()
    {
        Code = "Username already exists",
        Description = "Username already exists"
    }
};
userManagerMock
    .Setup(userManager => userManager.CreateAsync(It.IsAny<User>(), It.IsAny<string>()))
    .Returns(Task.FromResult(IdentityResult.Failed(identityErrors)));
userManagerMock
    .Setup(userManager => userManager.AddToRoleAsync(It.IsAny<User>(), It.IsAny<string>()));

Here, we set up the CreateAsync() method to return an IdentityError object, indicating that the provided username already exists in the system. As a result, the original registration page loads again and displays the error message:

var controller = new AccountController(mapperMock.Object, userManagerMock.Object, roleManagerMock.Object);
var result = (ViewResult)await controller.Register(userRegistrationModel);

Assert.Null(result.ViewName);

Conclusion

In this article, we have learned how to perform Unit Testing with UserManager and RoleManager in ASP.NET Core Identity. By using mock objects, we were able to isolate the behavior of the controller and test it in a controlled environment. As a result, we can be more confident in the correctness of our code and catch errors early on in the development process.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!