Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I stub/mock AuthenticationManager in MVC 6?

I'm trying to unit test an MVC 6 controller that directly interacts with this.HttpContext.Authentication for authentication. I'm not using the Identity Framework, but rather am interacting with Cookie Authentication middleware directly.

Following some examples I've seen by MS, I've been using DefaultHttpContext for testing purposes. The problem is that the AuthenticationManager property on DefaultHttpContext is read-only so I'm unsure how I can mock it.

If I don't mock it, I receive an error stating "No authentication handler is configured to handle the scheme: Cookies" in my tests when calling:

HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);

like image 949
Brian Vallelunga Avatar asked Dec 18 '22 21:12

Brian Vallelunga


2 Answers

I had the same problem and i finally did it with some level of mocking I use 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);

then you pass that controller context to you controller you want to test

 var controller = new TestedController(Your attribute)
 {
       ControllerContext = contContext
  };
like image 191
Simeraz Avatar answered Jan 09 '23 17:01

Simeraz


The accepted answer is now deprecated by MS who recommend against using the AuthenticationManager and instead using the extension methods. An alternative is below. Note that the additional two services added are because setting RequestServices removes some defaults which would otherwise be created by 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
like image 36
Lukos Avatar answered Jan 09 '23 16:01

Lukos