Testing UserManager and RoleManager in ASP.NET Core Identity
source link: https://code-maze.com/aspnetcore-identity-testing-usermanager-rolemanager/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Unit Testing With UserManager and RoleManager in ASP.NET Core Identity
Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API program and learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!
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.
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.
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.
Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API program and learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!
Share:
Recommend
-
11
Angular Authentication Functionality with ASP.NET Core Identity Posted by Marinko Spasojevic | Updated Date Dec 16, 2020 |
-
13
Stub User.Identity.IsAuthenticated in ASP Core I’m writing this article strictly because google do not have any obvious solutions in the hope it will be indexed and presented for fellow devs. We use identity serv...
-
15
Angular Email Confirmation with ASP.NET Core Identity Posted by Marinko Spasojevic | Updated Date Dec 21, 2020 |
-
6
Microsoft Identity Claims in ASP.NET Core: OperationAuthorization Requirement I didn't know this existed until a reader made me aware of it. Today, we update the menu systems with Microsoft Identity using the OperationAuthorizationR...
-
82
ASP.NET Core Identity AddDefaultIdentity vs AddIdentity The short version on the difference between AddDefaultIdentity and AddIdentity is the default part adds in all the built-in controllers and vi...
-
6
ASP.NET Core and Blazor Identity and State 16 Mar 2022 Over the past several weeks I’ve been wrestling with the way in which ASP.NET Core (aspnetcore) and server-side Blazor manage things like context, identity, and state....
-
6
Using Oracle Database with ASP.NET Core IdentityASP.NET Core Identity is a .NET interface that supports user and role authenticati...
-
4
Introduction to Identity Server with ASP.Net Core Identity Importance of security On every platform security is important, and especially tru...
-
5
Part-5 | Asp.Net Core Identity Series[.NET 7] | Google Authentication
-
5
Improvements to auth and identity in ASP.NET Core 8 Jeremy Likness...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK