Brian Vallelunga
Brian Vallelunga

Reputation: 10191

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

Upvotes: 4

Views: 3971

Answers (3)

JEuvin
JEuvin

Reputation: 1039

This is how you would do it using 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;

Upvotes: 0

Lukos
Lukos

Reputation: 1852

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

Upvotes: 4

Simeraz
Simeraz

Reputation: 96

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

Upvotes: 8

Related Questions