2016-07-26 78 views
5

我基于从MusiStore例如this测试试图单元测试像Login这种简单的方法在我的AccountController如何正确模仿IAuthenticationHandler而单元测试ASP.NET核心控制器

// 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 
} 

为此,我需要同时使用UserManagerSignInManager最终迫使我用写IAuthenticationHandler的替代品使用HttpAuthenticationFeature。最终测试原来是这样的:

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(){} 
} 

这:

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); 
    } 

} 

两个多嘲讽IAuthenticationHandler和创建多种类型实现IAuthenticationHandler在不同的方式,每个测试看起来有点太远对我来说,但我也想使用serviceProvider,并且不想模拟userManagersignInManager。虽然用这种方式编写的测试似乎有效,但我想知道是否有任何非复杂的方式使用CookieAuthenticationHandler或其他与app.UseIdentity()应用程序相同的方式。

回答

0

你可以嘲笑SignInManager,将它放在服务集合中,然后将一个调用设置为_signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false),以返回要为控制器测试的结果?

+0

我可以这样做了几次,但它不适合我,我认为这样的调用不会影响模拟的'HttpContext',所以更复杂的控制器可能不会以正确的方式运行。我也希望它尽可能地接近原始行为,我不擅长嘲笑物体。 – FluffyOwl

相关问题