Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何对HttpContext.SignInAsync()进行单元测试?_C#_Unit Testing_Asp.net Core_Asp.net Core Mvc_Asp.net Core 2.0 - Fatal编程技术网

C# 如何对HttpContext.SignInAsync()进行单元测试?

C# 如何对HttpContext.SignInAsync()进行单元测试?,c#,unit-testing,asp.net-core,asp.net-core-mvc,asp.net-core-2.0,C#,Unit Testing,Asp.net Core,Asp.net Core Mvc,Asp.net Core 2.0,我在单元测试中遇到了一些问题 DefaultHttpContext.RequestServices为空 我试图创建AuthenticationService对象,但不知道要传递哪些参数 我该怎么办?如何单元测试HttpContext.SignInAsync() 测试中的方法 public async Task<IActionResult> Login(LoginViewModel vm, [FromQuery]string returnUrl) { if (ModelStat

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

  • DefaultHttpContext.RequestServices
    为空
  • 我试图创建
    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);
    }
    

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

    context.RequestServices
        .GetRequiredService<IAuthenticationService>()
        .SignInAsync(context, scheme, principal, properties);
    
    你可以在他们的网站上了解如何使用最小起订量

    您可以像模拟其他依赖项一样轻松地模拟
    HttpContext
    ,但是如果存在一个不会导致不希望出现的行为的默认实现,那么使用它可以使安排工作变得更加简单

    例如,实际的
    IServiceProvider
    可以通过
    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
    
    //…为简洁起见删除了代码
    var authServiceMock=new Mock();
    authServiceMock
    .Setup(=>u.SignInAsync(It.IsAny(),It.IsAny(),It.IsAny(),It.IsAny())
    .Returns(Task.FromResult((object)null));
    var services=newservicecolection();
    AddSingleton(authServiceMock.Object);
    var控制器=新用户控制器(svc,null){
    ControllerContext=新的ControllerContext{
    HttpContext=新的DefaultHttpContext{
    //如何模拟请求服务?
    RequestServices=services.BuildServiceProvider();
    }
    }
    };
    //…为简洁起见,删除了代码
    

    这样,如果存在其他依赖项,则可以模拟这些依赖项并在服务集合中注册,以便根据需要解决它们。

    如果你们正在寻找NSubstitue示例(Asp.net core)

    iaauthenticationservice authenticationService=Substitute.For();
    认证服务
    .SignInAsync(Arg.Any(),Arg.Any(),Arg.Any(),Arg.Any(),
    返回(Task.FromResult((object)null));
    var serviceProvider=Substitute.For();
    var authSchemaProvider=Substitute.For();
    var systemClock=替换为();
    authSchemaProvider.GetDefaultAuthenticateSchemeAsync()。返回(Task.FromResult
    (新认证方案(“idp”、“idp”,
    类型(IAAuthenticationHandler));
    GetService(typeof(iaauthenticationservice))。返回(authenticationService);
    GetService(typeof(ISystemClock)).Returns(systemClock);
    GetService(typeof(iaAuthenticationSchemeProvider)).Returns(authSchemaProvider);
    context.RequestServices.Returns(serviceProvider);
    //你的表演到此为止
    //你的断言在这里
    
    这在.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);
        }
    }
    
    …然后我有一个实现供正常使用并继续测试

    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;
            }
        }
    }
    
    然后,我使用.NETCore的本机DI容器(在Startup.cs中)将所需的实现作为接口注入控制器的构造函数中


    回答得很好。我在使用以下代码时出错:“System.InvalidOperationException:没有注册类型为“Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory”的服务。”我还通过模拟ITempDataDictionary并将其分配给控制器来解决这个问题。TempData@JasonLearmouth此回答针对OP的特定目标类型
    iaAuthenticationService
    ,如果没有使用模拟设置任何其他类型,则会失败。当get服务调用返回null时,您得到的错误是默认错误。@Nkosi,我同意,这就是为什么我对您的答案投了赞成票而没有编辑它的原因。我发布了关于错误和解决方案的评论,因为仅仅根据错误消息,不太容易确定要模拟什么以及在哪里分配。我认为不应该模拟IdentityServer扩展方法中的依赖项。如果代码更改,那么测试将中断。在HttpContext上创建一个包装器,并测试是否执行了包装器上的方法,这样会更干净、更安全。要测试您实际登录的事实,可以通过集成测试来完成。对于我来说,可接受的解决方案在Core2.2中运行良好,我个人认为这是一种更好的方法。我认为您不应该在IdentityServer扩展方法中模拟依赖项。如果代码更改,那么测试将中断。在HttpContext上创建包装更干净、更安全
    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);
        }
    }
    
    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;
            }
        }
    }
    
    services.AddScoped<IHttpContextWrapper, DefaultHttpContextWrapper>();
    
    await _httpContextWrapper.SignInAsync(this, user.SubjectId, user.Username, props);