2016-07-26 10 views
5

ich Unit-Test von MusiStore Beispiel ein einfaches Verfahren wie Login dieses in meinem AccountController basierend auf this Test versuchen.Wie man richtig IAuthenticationHandler imitieren, während Unit-Tests ASP.NET Core-Controller

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

Dafür muss ich verwenden beide UserManager und SignInManager, die mich schließlich für IAuthenticationHandler einen Ersatz schreiben zu verwenden Kräfte in HttpAuthenticationFeature verwenden. Am Ende dreht Test wie folgt aus:

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

und diese:

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

} 

Sowohl mehrere spöttische IAuthenticationHandler und mehrere Klassen zu schaffen Implementierung IAuthenticationHandler für jeden Test auf unterschiedliche Weise ein wenig zu weit für mich sieht, aber ich möchte auch serviceProvider verwenden und nicht userManager und signInManager vortäuschen wollen. Während auf diese Weise geschriebene Tests zu funktionieren scheinen, möchte ich wissen, ob es eine superkomplizierte Möglichkeit gibt, CookieAuthenticationHandler oder irgendetwas anderes zu verwenden, das sich so verhält wie die Anwendung mit app.UseIdentity().

Antwort

0

Können Sie den SignInManager einfach überspielen, ihn in die Service-Sammlung stecken und den einen Anruf auf _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false) einrichten, um die Ergebnisse zurückzugeben, die Sie für Ihren Controller testen möchten?

+0

Ich kann und habe das ein paar Mal gemacht, aber es fühlt sich nicht richtig an und ich denke, dass solch ein Aufruf keinen verspotteten 'HttpContext' beeinflusst, so dass ein komplexerer Controller sich nicht richtig verhalten könnte. Außerdem möchte ich, dass es dem ursprünglichen Verhalten so nahe wie möglich kommt, und ich bin nicht sehr gut darin, Objekte zu verspotten. – FluffyOwl