如何对 HttpContext.SignInAsync() 进行单元测试? [英] How to unit test HttpContext.SignInAsync()?

查看:21
本文介绍了如何对 HttpContext.SignInAsync() 进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#L143" relSignno"代码

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

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

我该怎么办?如何对 HttpContext.SignInAsync() 进行单元测试?

被测方法

public async Task登录(LoginViewModel vm, [FromQuery]string returnUrl){如果(模型状态.IsValid){var user = await context.Users.FirstOrDefaultAsync(u => u.UserName == vm.UserName && u.Password == vm.Password);如果(用户!= null){var claim = new List{新索赔(ClaimTypes.Name,user.UserName)};var identity = new ClaimsIdentity(claims, "HappyDog");//这里等待 HttpContext.SignInAsync(new ClaimsPrincipal(identity));return Redirect(returnUrl ?? Url.Action("Index", "Goods"));}}返回视图(vm);}

到目前为止我尝试过的.

[测试方法]公共异步任务 LoginTest(){使用 (var context = new HappyDogContext(_happyDogOptions)){await context.Users.AddAsync(new User { Id = 1, UserName = "test", Password = "password", FacePicture = "FacePicture" });等待 context.SaveChangesAsync();var controller = new UserController(svc, null){ControllerContext = 新的 ControllerContext{HttpContext = 新的 DefaultHttpContext{//如何模拟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().SignInAsync(上下文,方案,主体,属性);

您可以通过创建从使用的接口派生的类来手动创建假/模拟,也可以使用模拟框架,例如 起订量

//...为简洁起见删除了代码var authServiceMock = new Mock();authServiceMock.Setup(_ => _.SignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult((object)null));var serviceProviderMock = new Mock();服务提供者模拟.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authServiceMock.Object);var controller = new UserController(svc, null) {ControllerContext = 新的 ControllerContext {HttpContext = 新的 DefaultHttpContext {//如何模拟RequestServices?RequestServices = serviceProviderMock.Object}}};//...为简洁起见删除了代码

您可以在他们的快速入门中了解如何使用 Moq>

您可以像模拟其他依赖项一样轻松地模拟 HttpContext ,但如果存在不会导致不期望行为的默认实现,那么使用它可以使事情变得更简单

例如,通过ServiceCollection

构建一个可以使用实际的IServiceProvider

//...为简洁起见删除了代码var authServiceMock = new Mock();authServiceMock.Setup(_ => _.SignInAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult((object)null));var services = new ServiceCollection();services.AddSingleton(authServiceMock.Object);var controller = new UserController(svc, null) {ControllerContext = 新的 ControllerContext {HttpContext = 新的 DefaultHttpContext {//如何模拟RequestServices?RequestServices = services.BuildServiceProvider();}}};//...为简洁起见删除了代码

这样,如果有其他依赖项,它们可以被模拟并注册到服务集合中,以便根据需要进行解析.

SignInAsync() Source Code

I ran into some problems with unit testing.

  1. DefaultHttpContext.RequestServices is null
  2. I tried to create the AuthenticationService object, but I do not know what parameters to pass

What should I do? How to unit test HttpContext.SignInAsync()?

Method under test

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

What I have tried so far.

[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 is an extension method that uses RequestServices, which is IServiceProvider. That is what you must mock.

context.RequestServices
    .GetRequiredService<IAuthenticationService>()
    .SignInAsync(context, scheme, principal, properties);

You can either create a fake/mock manually by creating classes that derive from the used interfaces or use a mocking framework like 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

You can read up on how to use Moq here at their Quick start

You could just as easily mocked the HttpContext as well like the other dependencies but if a default implementation exists that causes no undesired behavior, then using that can make things a lot simpler to arrange

For example, an actual IServiceProvider could have been used by building one via ServiceCollection

//...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 services = new ServiceCollection();
services.AddSingleton<IAuthenticationService>(authServiceMock.Object);

var controller = new UserController(svc, null) {
    ControllerContext = new ControllerContext {
        HttpContext = new DefaultHttpContext {
            // How mock RequestServices?
            RequestServices = services.BuildServiceProvider();
        }
    }
};

//...code removed for brevity

That way if there are other dependencies, they can be mocked and registered with the service collection so that they can be resolved as needed.

这篇关于如何对 HttpContext.SignInAsync() 进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆