如何在MVC 6中对AuthenticationManager进行存根/模拟?

4
我正在尝试对一个直接与this.HttpContext.Authentication进行身份验证交互的MVC 6控制器进行单元测试。我不使用Identity Framework,而是直接与Cookie Authentication中间件进行交互。
按照微软提供的一些示例,我正在使用DefaultHttpContext进行测试。问题在于DefaultHttpContext上的AuthenticationManager属性是只读的,因此我不确定如何模拟它。
如果我不模拟它,在调用以下代码时,我的测试会收到一个错误提示:“未配置任何身份验证处理程序来处理方案:Cookies”: HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);

这个问题有什么进展吗?我也遇到了类似的问题,正在尝试寻找可能的解决方案。 - Yousuf
3个回答

8

我曾经遇到同样的问题,最终通过一些模拟实现了它。 我使用了Moq。

var claimPrincipal = new ClaimsPrincipal();

var mockAuth = new Mock<AuthenticationManager>();
mockAuth.Setup(c => c.SignInAsync("YourScheme", claimPrincipal)).Returns(Task.FromResult("done"));

var mockContext = new Mock<HttpContext>();
            mockContext.Setup(c => c.Authentication).Returns(mockAuth.Object);

var fakeActionContext = new ActionContext(mockContext.Object, new RouteData(), new ControllerActionDescriptor());
var contContext = new ControllerContext(fakeActionContext);

然后将该控制器上下文传递给您想要测试的控制器。
 var controller = new TestedController(Your attribute)
 {
       ControllerContext = contContext
  };

4
现在,微软不推荐使用AuthenticationManager,而是建议使用扩展方法。下面是一种替代方案。请注意,添加的两个额外服务是因为设置RequestServices会删除一些默认值,否则这些默认值将由DefaultHttpContext创建。
var context = new ControllerContext();
context.HttpContext = new DefaultHttpContext();
context.HttpContext.User = userMock.Object;  // If needed
context.HttpContext.Session = sessionMock.Object;  // If needed

var authManager = new Mock<IAuthenticationService>();
authManager.Setup(s => s.SignOutAsync(It.IsAny<HttpContext>(), 
            CookieAuthenticationDefaults.AuthenticationScheme, 
            It.IsAny<Microsoft.AspNetCore.Authentication.AuthenticationProperties>())).
            Returns(Task.FromResult(true));
var servicesMock = new Mock<IServiceProvider>();
servicesMock.Setup(sp => sp.GetService(typeof(IAuthenticationService))).Returns(authManager.Object);
servicesMock.Setup(sp => sp.GetService(typeof(IUrlHelperFactory))).Returns(new UrlHelperFactory());
servicesMock.Setup(sp => sp.GetService(typeof(ITempDataDictionaryFactory))).Returns(new TempDataDictionaryFactory(new SessionStateTempDataProvider()));

context.HttpContext.RequestServices = servicesMock.Object;
// Use context to pass to your controller

0

以下是使用FakeItEasy的方法:

var actionContext = new ActionContext
        {
            HttpContext = new DefaultHttpContext(),
            RouteData = new RouteData(),
            ActionDescriptor = new ControllerActionDescriptor()
        };

// Creating AuthenticationService and Return calls for the Authentication factory
            var authManager = A.Fake<IAuthenticationService>();
            var authProperties = new Microsoft.AspNetCore.Authentication.AuthenticationProperties();
            var servicesMock = A.Fake<IServiceProvider>();
            A.CallTo(() => authManager.SignOutAsync(actionContext.HttpContext, CookieAuthenticationDefaults.AuthenticationScheme, authProperties)).Returns(Task.FromResult(true));
            A.CallTo(() => servicesMock.GetService(typeof(IAuthenticationService))).Returns(authManager);
            A.CallTo(() => servicesMock.GetService(typeof(IUrlHelperFactory))).Returns(new UrlHelperFactory());
            A.CallTo(() => servicesMock.GetService(typeof(ITempDataDictionaryFactory))).Returns(new TempDataDictionaryFactory(new SessionStateTempDataProvider()));

            var concon = new ControllerContext(actionContext);
            concon.HttpContext.RequestServices = servicesMock;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接