如何单元测试HttpContext.SignInAsync()?

Her*_*ong 12 c# unit-testing .net-core asp.net-core asp.net-core-2.0

SignInAsync()源代码

我遇到了单元测试的一些问题.

  1. DefaultHttpContext.RequestServicesnull
  2. 我试图创建AuthenticationService对象,但我不知道要传递什么参数

我该怎么办?如何进行单元测试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);
}
Run Code Online (Sandbox Code Playgroud)

到目前为止我尝试过的.

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 20

HttpContext.SignInAsync是一种使用的扩展方法RequestServices,即IServiceProvider.那是你必须嘲笑的.

context.RequestServices
    .GetRequiredService<IAuthenticationService>()
    .SignInAsync(context, scheme, principal, properties);
Run Code Online (Sandbox Code Playgroud)

您可以通过创建从使用的接口派生的类或使用类似的模拟框架手动创建伪/模拟 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
Run Code Online (Sandbox Code Playgroud)

您可以HttpContext像其他依赖项一样轻松地模拟它.

您可以在快速入门时阅读有关如何使用Moq的信息

  • 真的很好的答案. (3认同)
  • 使用此代码“ System.InvalidOperationException:没有注册类型'Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory'的服务”,我收到了一个错误。我也通过模拟ITempDataDictionary并将其分配给controller.TempData来解决此问题。 (2认同)