How to correctly simulate IAuthenticationHandler during unit testing of ASP.NET Core controller

I am trying to use the unit test simple method, for example Login in my AccountController based on this test. MusiStore example .

 // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginArgumentsModel model) { if (!ModelState.IsValid) { return BadRequest(); } var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { return Ok(); } return StatusCode(422); // Unprocessable Entity } 

To do this, I need to use both UserManager and SignInManager , which ultimately forces me to use the entry for IAuthenticationHandler for use in the HttpAuthenticationFeature . At the end, the test is obtained as follows:

 public class AccountControllerTestsFixture : IDisposable { public IServiceProvider BuildServiceProvider(IAuthenticationHandler handler) { var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider(); var services = new ServiceCollection(); services.AddOptions(); services.AddDbContext<ApplicationDbContext>(b => b.UseInMemoryDatabase().UseInternalServiceProvider(efServiceProvider)); services.AddIdentity<ApplicationUser, IdentityRole>(o => { o.Password.RequireDigit = false; o.Password.RequireLowercase = false; o.Password.RequireUppercase = false; o.Password.RequireNonAlphanumeric = false; o.Password.RequiredLength = 3; }).AddEntityFrameworkStores<ApplicationDbContext>(); // IHttpContextAccessor is required for SignInManager, and UserManager var context = new DefaultHttpContext(); context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature { Handler = handler }); services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor() { HttpContext = context }); return services.BuildServiceProvider(); } public Mock<IAuthenticationHandler> MockSignInHandler() { var handler = new Mock<IAuthenticationHandler>(); handler.Setup(o => o.AuthenticateAsync(It.IsAny<AuthenticateContext>())).Returns<AuthenticateContext>(c => { c.NotAuthenticated(); return Task.FromResult(0); }); handler.Setup(o => o.SignInAsync(It.IsAny<SignInContext>())).Returns<SignInContext>(c => { c.Accept(); return Task.FromResult(0); }); return handler; } public void Dispose(){} } 

and this:

 public class AccountControllerTests : IClassFixture<AccountControllerTestsFixture> { private AccountControllerTestsFixture _fixture; public AccountControllerTests(AccountControllerTestsFixture fixture) { _fixture = fixture; } [Fact] public async Task Login_When_Present_Provider_Version() { // Arrange var mockedHandler = _fixture.MockSignInHandler(); IServiceProvider serviceProvider = _fixture.BuildServiceProvider(mockedHandler.Object); var userName = "Flattershy"; var userPassword = "Angel"; var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, userName) }; var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>(); var userManagerResult = await userManager.CreateAsync(new ApplicationUser() { Id = userName, UserName = userName, TwoFactorEnabled = false }, userPassword); Assert.True(userManagerResult.Succeeded); var signInManager = serviceProvider.GetRequiredService<SignInManager<ApplicationUser>>(); AccountController controller = new AccountController(userManager, signInManager); // Act var model = new LoginArgumentsModel { UserName = userName, Password = userPassword }; var result = await controller.Login(model) as Microsoft.AspNetCore.Mvc.StatusCodeResult; // Assert Assert.Equal((int)System.Net.HttpStatusCode.OK, result.StatusCode); } } 

Both multiple IAuthenticationHandler bullying and creating multiple classes that implement the IAuthenticationHandler for each test in different ways look too far for me, but I also want to use serviceProvider and don't want to scoff at the UserManager and SignInManager . Although the tests written in this way seem to work, I want to know if there is any complicated way to use the CookieAuthenticationHandler or anything else that behaves the way the application does with app.UseIdentity() .

+5
source share
1 answer

Can you just make fun of the SignInManager, insert it into the services collection and set up one call on _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false) to return the results you want to check for your controller?

0
source

All Articles