私のAccountController
MusiStore例からのテストに基づいてこのような簡単なメソッドをテストしようとしています。そのためにどのように適切に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
}
私は最終的にHttpAuthenticationFeature
で使用するためのIAuthenticationHandler
の代用を書く使用するように私を強制的に両方UserManager
とSignInManager
を使用する必要があります。最後に、テストは次のように判明:
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を使用したいので、userManager
とsignInManager
をモックしたくありません。このように書かれたテストはうまくいくように見えますが、CookieAuthenticationHandler
などの複雑な方法や、app.UseIdentity()
を使ってアプリケーションを動作させる何かがあるかどうかを知りたいと思っています。
私はこれを数回行うことができますが、それは私にはうってつけではないと思います。そのような呼び出しは模倣された 'HttpContext'には影響しないと思うので、より複雑なコントローラは適切な振る舞いをしません。また、私はそれができるだけ元の動作に近いものにしたい、私はオブジェクトを嘲笑ではあまりよくありません。 – FluffyOwl