ユニットテストでいくつかの問題が発生しました。
DefaultHttpContext.RequestServices
_ isnullAuthenticationService
オブジェクトを作成しようとしましたが、渡すパラメーターがわかりません私は何をすべきか? HttpContext.SignInAsync()
をユニットテストする方法は?
テスト中のメソッド
_public async Task<IActionResult> Login(LoginViewModel vm, [FromQuery]string returnUrl)
{
if (ModelState.IsValid)
{
var user = await context.Users.FirstOrDefaultAsync(u => u.UserName == vm.UserName && u.Password == vm.Password);
if (user != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName)
};
var identity = new ClaimsIdentity(claims, "HappyDog");
// here
await HttpContext.SignInAsync(new ClaimsPrincipal(identity));
return Redirect(returnUrl ?? Url.Action("Index", "Goods"));
}
}
return View(vm);
}
_
私がこれまでに試したこと。
_[TestMethod]
public async Task LoginTest()
{
using (var context = new HappyDogContext(_happyDogOptions))
{
await context.Users.AddAsync(new User { Id = 1, UserName = "test", Password = "password", FacePicture = "FacePicture" });
await context.SaveChangesAsync();
var controller = new UserController(svc, null)
{
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
// How mock RequestServices?
// RequestServices = new AuthenticationService()?
}
}
};
var vm = new LoginViewModel { UserName = "test", Password = "password" };
var result = await controller.Login(vm, null) as RedirectResult;
Assert.AreEqual("/Goods", result.Url);
}
}
_
HttpContext.SignInAsync
は、RequestServices
を使用する拡張メソッドです。これはIServiceProvider
です。それはあなたが嘲笑しなければならないものです。
context.RequestServices
.GetRequiredService<IAuthenticationService>()
.SignInAsync(context, scheme, principal, properties);
使用するインターフェースから派生するクラスを作成して偽/モックを手動で作成するか、 Moq
のようなモックフレームワークを使用することができます。
//...code removed for brevity
var authServiceMock = new Mock<IAuthenticationService>();
authServiceMock
.Setup(_ => _.SignInAsync(It.IsAny<HttpContext>(), It.IsAny<string>(), It.IsAny<ClaimsPrincipal>(), It.IsAny<AuthenticationProperties>()))
.Returns(Task.FromResult((object)null));
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(_ => _.GetService(typeof(IAuthenticationService)))
.Returns(authServiceMock.Object);
var controller = new UserController(svc, null) {
ControllerContext = new ControllerContext {
HttpContext = new DefaultHttpContext {
// How mock RequestServices?
RequestServices = serviceProviderMock.Object
}
}
};
//...code removed for brevity
他の依存関係と同じように、HttpContext
を簡単にモックすることができます。
Moqの使用方法については、こちらの クイックスタート で確認できます。
NSubstitueの例(Asp.netコア)をお探しの場合。
IAuthenticationService authenticationService = Substitute.For<IAuthenticationService>();
authenticationService
.SignInAsync(Arg.Any<HttpContext>(), Arg.Any<string>(), Arg.Any<ClaimsPrincipal>(),
Arg.Any<AuthenticationProperties>()).Returns(Task.FromResult((object) null));
var serviceProvider = Substitute.For<IServiceProvider>();
var authSchemaProvider = Substitute.For<IAuthenticationSchemeProvider>();
var systemClock = Substitute.For<ISystemClock>();
authSchemaProvider.GetDefaultAuthenticateSchemeAsync().Returns(Task.FromResult
(new AuthenticationScheme("idp", "idp",
typeof(IAuthenticationHandler))));
serviceProvider.GetService(typeof(IAuthenticationService)).Returns(authenticationService);
serviceProvider.GetService(typeof(ISystemClock)).Returns(systemClock);
serviceProvider.GetService(typeof(IAuthenticationSchemeProvider)).Returns(authSchemaProvider);
context.RequestServices.Returns(serviceProvider);
// Your act goes here
// Your assert goes here
これは、.NET Core 2.2では機能しませんでした。それでも、別のインターフェイスであるISystemClockが必要です。だから私は単に別のアプローチを取ることに決めました。つまり、次のように全体をラップすることです。
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
namespace Utilities.HttpContext
{
public interface IHttpContextWrapper
{
Task SignInAsync(Controller controller, string subject, string name, AuthenticationProperties props);
}
}
...そして、通常の使用とテスト用の実装が1つあります。
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
namespace Utilities.HttpContext
{
public class DefaultHttpContextWrapper : IHttpContextWrapper
{
public async Task SignInAsync(Controller controller, string subject, string name, AuthenticationProperties props)
{
await controller.HttpContext.SignInAsync(subject, name, props);
}
}
}
...そして偽の実装:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
namespace Utilities.HttpContext
{
public class FakeHttpContextWrapper : IHttpContextWrapper
{
public Task SignInAsync(Controller controller, string subject, string name, AuthenticationProperties props)
{
return Task.CompletedTask;
}
}
}
次に、.NET CoreのネイティブDIコンテナー(Startup.cs内)を使用して、コントローラーのコンストラクターにインターフェイスとして目的の実装を挿入します。
services.AddScoped<IHttpContextWrapper, DefaultHttpContextWrapper>();
最後に、呼び出しは次のようになります(これを使用してコントローラーを渡します)。
await _httpContextWrapper.SignInAsync(this, user.SubjectId, user.Username, props);