xunit - how to get HttpContext.User.Identity in unit tests - c#

I added a method to my controllers to get the user-id from the JWT token in the HttpContext. In my unit tests the HttpContext is null, so I get an exception.
How can I solve the problem? Is there a way to moq the HttpContext?
Here is the method to get the user in my base controller
protected string GetUserId()
{
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
One of my tests look like this
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup)
{
// Arrange
UsergroupController controller = new UsergroupController(context, mapper);
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
//....
}

There really is no need to have to mock the HttpContext in this particular case.
Use the DefaultHttpContext and set the members necessary to exercise the test to completion
For example
[Theory]
[MemberData(nameof(TestCreateUsergroupItemData))]
public async Task TestPostUsergroupItem(Usergroup usergroup) {
// Arrange
//...
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity); //add claims as needed
//...then set user and other required properties on the httpContext as needed
var httpContext = new DefaultHttpContext() {
User = contextUser;
};
//Controller needs a controller context to access HttpContext
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
UsergroupController controller = new UsergroupController(context, mapper) {
ControllerContext = controllerContext,
};
// Act
var controllerResult = await controller.Post(usergroup).ConfigureAwait(false);
// Assert
....
}

First of all, I would suggest you to use IHttpContextAccessor to access HttpContext and inject via Dependency Injection instead of using HttpContext directly. You can follow this Microsoft documentation to understand usage and injection of IHttpContextAccessor.
With the above code, your code will looks as follows to inject IHttpContextAccessor
private IHttpContextAccessor httpContextAccessor;
public class UsergroupController(IHttpContextAccessor httpContextAccessor, ...additional parameters)
{
this.httpContextAccessor = httpContextAccessor;
//...additional assignments
}
Once IHttpContextAccessor is injected, you can access the Identity as this.httpContextAccessor.HttpContext.User.Identity
So the GetUserId should change as
protected string GetUserId()
{
if (this.httpContextAccessor.HttpContext.User.Identity is ClaimsIdentity identity)
{
IEnumerable<Claim> claims = identity.Claims;
return claims.ToList()[0].Value;
}
return "";
}
With above change, now you can easily inject the mock of IHttpContextAccessor for unit testing. You can use the below code to create the mock:
private static ClaimsPrincipal user = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[] { new Claim("MyClaim", "MyClaimValue") },
"Basic")
);
private static Mock<IHttpContextAccessor> GetHttpContextAccessor()
{
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock.Setup(h => h.HttpContext.User).Returns(user);
return httpContextAccessorMock;
}
With the above setup, in your test method, you can inject the mock of IHttpContextAccessor while instantiating the object of UsergroupController.

As a follow up to the comment by #Nkosi:
there's no requirement to DI the context as you can set it during testing like so:
var identity = new GenericIdentity("some name", "test");
var contextUser = new ClaimsPrincipal(identity);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = contextUser }
};

Related

How to mock AuthenticationStateProvider

I have a class that takes an AuthenticationStateProvider in the constructor. I'd like to write unit test and mock this.
I'd like to be able to set what user is returned from the call GetAuthenticationStateAsync.
const string userId = "123";
const string userName = "John Doe";
const string email = "john.doe#test.com";
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId),
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Email, email),
};
var identity = new Mock<ClaimsIdentity>(claims);
var principal = new Mock<ClaimsPrincipal>(identity.Object);
var mockOfAuthenticationStateProvider = new Mock<AuthenticationStateProvider>();
var mockOfAuthState = new Mock<AuthenticationState>();
mockOfAuthenticationStateProvider.Setup(p =>
p.GetAuthenticationStateAsync()).Returns(Task.FromResult(mockOfAuthState.Object));
I get this errormessage:
Testfunction threw exception: System.ArgumentException: Can not
instantiate proxy of class:
Microsoft.AspNetCore.Components.Authorization.AuthenticationState.
Could not find a parameterless constructor. (Parameter
'constructorArguments') ---> System.MissingMethodException:
Constructor on type 'Castle.Proxies.AuthenticationStateProxy' not
found.
AuthenticationStateProvider is abstract, so if you can't mock it you can create an implementation of it, like this:
public class FakeAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly ClaimsPrincipal _principal;
public FakeAuthenticationStateProvider(ClaimsPrincipal principal)
{
_principal = principal;
}
// This static method isn't really necessary. You could call the
// constructor directly. I just like how it makes it more clear
// what the fake is doing within the test.
public static FakeAuthenticationStateProvider ForPrincipal(ClaimsPrincipal principal)
{
return new FakeAuthenticationStateProvider(principal);
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(_principal));
}
}
You can set it up like this:
const string userId = "123";
const string userName = "John Doe";
const string email = "john.doe#test.com";
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId),
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Email, email),
};
// These don't need to be mocks. If they are the test likely
// won't behave correctly.
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var authenticationStateProvider =
FakeAuthenticationStateProvider.ForPrincipal(principal);
and then pass it to any other class that depends on AuthenticationStateProvider.
When we create a fake implementation instead of a mock it's reusable and also easier to set up, so it keeps the test a little bit smaller.
For example, if the reason you're setting this up is so that the mock/fake returns a user with certain claims, you could have the fake just take a set of claims in its constructor. Then the constructor does the rest of the work, making your test even smaller.
For example,
public class FakeAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly ClaimsPrincipal _principal;
public FakeAuthenticationStateProvider(ClaimsPrincipal principal)
{
_principal = principal;
}
public static FakeAuthenticationStateProvider ForPrincipal(ClaimsPrincipal principal)
{
return new FakeAuthenticationStateProvider(principal);
}
public static FakeAuthenticationStateProvider ThatReturnsClaims(params Claim[] claims)
{
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
return new FakeAuthenticationStateProvider(principal);
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(_principal));
}
}
Now your test can have less clutter:
var claims = new []
{
new Claim(ClaimTypes.NameIdentifier, "123"),
new Claim(ClaimTypes.Name, "John Doe"),
new Claim(ClaimTypes.Email, "john.doe#test.com"),
};
var authenticationStateProvider = FakeAuthenticationStateProvider.ThatReturnsClaims(claims);
I usually end up with a folder called "Fakes" in my test project for them.

How can I test a controller that uses identity with a readonly property?

I have the following code:
[Route("resources/avatar")]
[ApiController]
public class AvatarController : ControllerBase
{
private readonly ApplicationDbContext database;
private readonly IWebHostEnvironment environment;
private readonly IUserManagerWrapper userManagerWrapper;
public AvatarController(IUserManagerWrapper userManagerWrapper, IWebHostEnvironment environment,
ApplicationDbContext database)
{
this.userManagerWrapper = userManagerWrapper;
this.environment = environment;
this.database = database;
}
[HttpGet]
[Route("")]
public async Task<IActionResult> Index()
{
if (User == null) return DefaultImage();
var user = await this.userManagerWrapper.GetUserAsync(User);
if ((user?.Avatar?.Length ?? 0) == 0) return DefaultImage();
return File(user.Avatar, "image/jpeg");
}
}
I have an issue with testing this Index Page.
User is a property that comes from ControllerBase and is of type ClaimsPrincipal.
I used a wrapper where I would wrap the usermanager and then use an interface that I would mock.
The problem with this approach is I cannot set this ClaimsPrincipal to null because it is a read-only.
This was my test:
[TestFixture]
public class AvatarController_Should
{
[Test]
public async Task IndexReturnsDefaultImage()
{
var hostingEnvironmentMock = new Mock<IWebHostEnvironment>();
var dabataseName = nameof(IndexReturnsDefaultImage);
var options = AvatarTestUtil.GetOptions(dabataseName);
var userManagerWrapperMock = new Mock<IUserManagerWrapper>();
using (var actAndAssertContext = new ApplicationDbContext(options))
{
var sut = new AvatarController(userManagerWrapperMock.Object, hostingEnvironmentMock.Object, actAndAssertContext);
}
}
}
public class AvatarTestUtil
{
public static DbContextOptions<ApplicationDbContext> GetOptions(string databaseName)
{
var serviceCollection = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
return new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName)
.UseInternalServiceProvider(serviceCollection)
.Options;
}
}
}
I am open to using a completely new approach.
This was how I used to do test on the identity before, but I am stuck now.
Looking at the source code for ControllerBase, we can see User is defined as so
public ClaimsPrincipal User => HttpContext?.User;
So the User actually comes from HttpContext. But HttpContext is readonly as well. Digging into the source code deeper, though, we can see that HttpContext is derived from ControllerContext
public HttpContext HttpContext => ControllerContext.HttpContext;
Alas! ControllerContext actually has a setter in the concrete implementation!
public ControllerContext ControllerContext { get; set; }
We could set up a whole new ControllerContext here if we wanted. But we really just need ControllerContext.User. Luckily, that has a setter too. Since you only really need to set the User, we can do so directly here rather than newing up another ControllerContext.
using (var actAndAssertContext = new ApplicationDbContext(options))
{
var sut = new AvatarController(userManagerWrapperMock.Object, hostingEnvironmentMock.Object, actAndAssertContext);
sut.ControllerContext.HttpContext.User = null;
}

Is it possible to write a test that can test an AuthorizationPolicy Object?

I've got a policy that I want to test in C#
public class WorkflowCreatePolicy
{
public AuthorizationPolicy AuthorizationPolicy =>
new AuthorizationPolicyBuilder()
.RequireClaim("scope", "WorkflowAdmin")
.Build();
}
Does anyone know of a way to test the AuthorizationPolicy to confirm that the scope "WorkflowAdmin" is successful and all others aren't?
This is what I see when I inspect the object:
I've managed to find this website: Authorization Handler Unit Tests but its talking about testing handlers and has code that marks the auth attempt as successful.
i'm not sure if this is getting close or not. It currently doesn't pass
[Test]
public void GivenPolicyName_WhenICallPolicyChecks_ThenItPasses()
{
ClaimsPrincipal user = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim(CustomClaims.Scope, "WorkflowAdmin") }));
WorkflowCreatePolicy workflowCreatePolicy = new WorkflowCreatePolicy();
AuthorizationHandlerContext authorizationHandlerContext = new AuthorizationHandlerContext(workflowCreatePolicy.AuthorizationPolicy.Requirements, user, null);
Assert.That(authorizationHandlerContext.HasSucceeded, Is.EqualTo(true));
}
See this test in the ASP.NET Core Security Unit Tests. I've taken the pattern from it and applied it to your policy.
[Fact]
public async Task ShouldAllowIfScopeClaimWorkflowAdminIsPresent()
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.AddAuthorization(options =>
{
options.AddPolicy("SomePolicyName", new WorkflowCreatePolicy()
.AuthorizationPolicy);
});
});
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] { new Claim("scope", "WorkflowAdmin") }));
// Act
var allowed = await authorizationService.AuthorizeAsync(user, "SomePolicyName");
// Assert
Assert.True(allowed.Succeeded);
}
private IAuthorizationService BuildAuthorizationService(
Action<IServiceCollection> setupServices = null)
{
var services = new ServiceCollection();
services.AddAuthorization();
services.AddLogging();
services.AddOptions();
setupServices?.Invoke(services);
return services.BuildServiceProvider().GetRequiredService<IAuthorizationService>();
}
Under the hood an AuthorizationPolicy is just a collection of authorization handlers. Methods like RequireClaim add handlers build by Microsoft to the collection. In this case the ClaimsAuthorizationRequirement which inherits from AuthorizationHandler.
To validate if a user passes an AuthorizationPolicy you need an AuthorizationService which will call all policies. The DefaultAuthorizationService will stop after the first handler has failed to authenticate the user. If you do not register another AuthorizationService this one will be used.
So you could build the AuthorizationService by yourself and call the AuthorizeAsync method on it. Just be aware that you need to register your custom AuthorizationHandler's too if you want to test against them.
private static async Task<bool> CanAuthorizeUserWithPolicyAsync(ClaimsPrincipal user, AuthorizationPolicy policy)
{
var handlers = policy.Requirements.Select(x => x as IAuthorizationHandler).ToArray();
// add your custom authorization handlers here to the `handlers` collection
var authorizationOptions = Options.Create(new AuthorizationOptions());
authorizationOptions.Value.AddPolicy(nameof(policy), policy);
var policyProvider = new DefaultAuthorizationPolicyProvider(authorizationOptions);
var handlerProvider = new DefaultAuthorizationHandlerProvider(handlers);
var contextFactory = new DefaultAuthorizationHandlerContextFactory();
var authorizationService = new DefaultAuthorizationService(
policyProvider,
handlerProvider,
new NullLogger<DefaultAuthorizationService>(),
contextFactory,
new DefaultAuthorizationEvaluator(),
authorizationOptions);
var result = await authorizationService.AuthorizeAsync(user, policy);
return result.Succeeded;
}
You can use this method like the following.
var user = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim("scope", "WorkflowAdmin") }));
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("scope", "WorkflowAdmin")
.Build();
Assert.That(await CanAuthorizeUserWithPolicyAsync(user, policy), Is.EqualTo(true));
Found it :)
[TestFixture]
public class WorkflowCreatePolicyTests
{
[Test]
public void GivenAuthorizationPolicy_WhenICheckTheClaimScopes_ThenItHasUserAdmin()
{
AuthorizationPolicy authorizationPolicy = new WorkflowCreatePolicy().AuthorizationPolicy;
ClaimsAuthorizationRequirement claimsAuthorizationRequirement = authorizationPolicy.Requirements
.FirstOrDefault(x => (x as ClaimsAuthorizationRequirement)?.ClaimType == "scope")
as ClaimsAuthorizationRequirement;
Assert.That(claimsAuthorizationRequirement?.AllowedValues, Contains.Item("WorkflowAdmin"));
}
}
IMHO that's something you would want to test with integration tests.
Unit testing it is covered by the ASP.NET Core and at the end of the day you want to make sure that access to your end-points are protected as you expect.
That you can do with TestServer.
It's the composition of the app that shall be tested at that point: Making sure that calling your end-points without the desired claim will result in a 403.

Mocking IPrincipal in ASP.NET Core

I have an ASP.NET MVC Core application that I am writing unit tests for. One of the action methods uses User name for some functionality:
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
which obviously fails in the unit test. I looked around and all suggestions are from .NET 4.5 to mock HttpContext. I am sure there is a better way to do that. I tried to inject IPrincipal, but it threw an error; and I even tried this (out of desperation, I suppose):
public IActionResult Index(IPrincipal principal = null) {
IPrincipal user = principal ?? User;
SettingsViewModel svm = _context.MySettings(user.Identity.Name);
return View(svm);
}
but this threw an error as well.
Couldn't find anything in the docs either...
The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.
The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:
var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));
var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};
When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).
In previous versions you could have set User directly on the controller, which made for some very easy unit tests.
If you look at the source code for ControllerBase you will notice that the User is extracted from HttpContext.
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;
and the controller accesses the HttpContext via ControllerContext
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;
You will notice that these two are read only properties. The good news is that ControllerContext property allows for setting it's value so that will be your way in.
So the target is to get at that object. In Core HttpContext is abstract so it is a lot easier to mock.
Assuming a controller like
public class MyController : Controller {
IMyContext _context;
public MyController(IMyContext context) {
_context = context;
}
public IActionResult Index() {
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
return View(svm);
}
//...other code removed for brevity
}
Using Moq, a test could look like this
public void Given_User_Index_Should_Return_ViewResult_With_Model() {
//Arrange
var username = "FakeUserName";
var identity = new GenericIdentity(username, "");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
var model = new SettingsViewModel() {
//...other code removed for brevity
};
var mockContext = new Mock<IMyContext>();
mockContext.Setup(m => m.MySettings(username)).Returns(model);
var controller = new MyController(mockContext.Object) {
ControllerContext = new ControllerContext {
HttpContext = mockHttpContext.Object
}
};
//Act
var viewResult = controller.Index() as ViewResult;
//Assert
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
Assert.AreEqual(model, viewResult.Model);
}
There is also the possibility to use the existing classes, and mock only when needed.
var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user.Object
}
};
In my case, I needed to make use of Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Name and some business logic sitting outside of the controller. I was able to use a combination of Nkosi's, Calin's and Poke's answer for this:
var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);
var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();
var controller = new MyController(...);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
User = mockPrincipal.Object
};
var result = controller.Get() as OkObjectResult;
//Assert results
mockAuthHandler.Verify();
I want to hit my Controllers directly and just use DI like AutoFac. To do this I first registering ContextController.
var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
User = new GenericPrincipal(identity, null)
};
var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);
Next I enable property injection when I register the Controllers.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();
Then User.Identity.Name is populated, and I do not need to do anything special when calling a method on my Controller.
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
var requestedBy = User.Identity?.Name;
..................
I would look to implement an Abstract Factory Pattern.
Create an interface for a factory specifically for providing user names.
Then provide concrete classes, one which provides User.Identity.Name, and one that provides some other hard coded value that works for your tests.
You can then use the appropriate concrete class depending on production versus test code. Perhaps looking to pass the factory in as a parameter, or switching to the correct factory based on some configuration value.
interface IUserNameFactory
{
string BuildUserName();
}
class ProductionFactory : IUserNameFactory
{
public BuildUserName() { return User.Identity.Name; }
}
class MockFactory : IUserNameFactory
{
public BuildUserName() { return "James"; }
}
IUserNameFactory factory;
if(inProductionMode)
{
factory = new ProductionFactory();
}
else
{
factory = new MockFactory();
}
SettingsViewModel svm = _context.MySettings(factory.BuildUserName());
I got a brownfield .net 4.8 project that I needed to convert to .net 5.0 and I wanted to keep as much of the original code as possible, including the unit-/integration tests. The test for Controllers relied on the Context a lot so I created this Extension method to enable setting tokens, claims and headers:
public static void AddContextMock(
this ControllerBase controller,
IEnumerable<(string key, string value)> claims = null,
IEnumerable<(string key, string value)> tokens = null,
IEnumerable<(string key, string value)> headers = null)
{
HttpContext mockContext = new DefaultHttpContext();
if(claims != null)
{
mockContext.User = SetupClaims(claims);
}
if(tokens != null)
{
mockContext.RequestServices = SetupTokens(tokens);
}
if(headers != null)
{
SetupHeaders(mockContext, headers);
}
controller.ControllerContext = new ControllerContext()
{
HttpContext = mockContext
};
}
private static void SetupHeaders(HttpContext mockContext, IEnumerable<(string key, string value)> headers)
{
foreach(var header in headers)
{
mockContext.Request.Headers.Add(header.key, header.value);
}
}
private static ClaimsPrincipal SetupClaims(IEnumerable<(string key, string value)> claimValues)
{
var claims = claimValues.Select(c => new Claim(c.key, c.value));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "mock"));
}
private static IServiceProvider SetupTokens(IEnumerable<(string key, string value)> tokenValues)
{
var mockServiceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
var tokens = tokenValues.Select(t => new AuthenticationToken { Name = t.key, Value = t.value });
authResult.Properties.StoreTokens(tokens);
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
mockServiceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
return mockServiceProvider.Object;
}
This uses Moq but can be adapted to other mocking frameworks. The authentication type is hardcoded to "mock" since I rely on default authentication but this could be supplied as well.
It is used as such:
_controllerUnderTest.AddContextMock(
claims: new[]
{
(ClaimTypes.Name, "UserName"),
(ClaimTypes.MobilePhone, "1234"),
},
tokens: new[]
{
("access_token", "accessTokenValue")
},
headers: new[]
{
("header", "headerValue")
});
If you're using Razor pages and want to override the claims:
[SetUp]
public void Setup()
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new("dateofbirth", "2000-10-10"),
new("surname", "Smith") },
"mock"));
_razorModel = new RazorModel()
{
PageContext = new PageContext
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
}

How to mock Controller.User using moq

I have a couple of ActionMethods that queries the Controller.User for its role like this
bool isAdmin = User.IsInRole("admin");
acting conveniently on that condition.
I'm starting to make tests for these methods with code like this
[TestMethod]
public void HomeController_Index_Should_Return_Non_Null_ViewPage()
{
HomeController controller = new HomePostController();
ActionResult index = controller.Index();
Assert.IsNotNull(index);
}
and that Test Fails because Controller.User is not set.
Any idea?
You need to Mock the ControllerContext, HttpContextBase and finally IPrincipal to mock the user property on Controller. Using Moq (v2) something along the following lines should work.
[TestMethod]
public void HomeControllerReturnsIndexViewWhenUserIsAdmin() {
var homeController = new HomeController();
var userMock = new Mock<IPrincipal>();
userMock.Expect(p => p.IsInRole("admin")).Returns(true);
var contextMock = new Mock<HttpContextBase>();
contextMock.ExpectGet(ctx => ctx.User)
.Returns(userMock.Object);
var controllerContextMock = new Mock<ControllerContext>();
controllerContextMock.ExpectGet(con => con.HttpContext)
.Returns(contextMock.Object);
homeController.ControllerContext = controllerContextMock.Object;
var result = homeController.Index();
userMock.Verify(p => p.IsInRole("admin"));
Assert.AreEqual(((ViewResult)result).ViewName, "Index");
}
Testing the behaviour when the user isn't an admin is as simple as changing the expectation set on the userMock object to return false.
Using Moq version 3.1 (and NUnit):
[Test]
public void HomeController_Index_Should_Return_Non_Null_ViewPage()
{
// Assign:
var homeController = new HomeController();
Mock<ControllerContext> controllerContextMock = new Mock<ControllerContext>();
controllerContextMock.Setup(
x => x.HttpContext.User.IsInRole(It.Is<string>(s => s.Equals("admin")))
).Returns(true);
homeController.ControllerContext = controllerContextMock.Object;
// Act:
ActionResult index = homeController.Index();
// Assert:
Assert.IsNotNull(index);
// Place other asserts here...
controllerContextMock.Verify(
x => x.HttpContext.User.IsInRole(It.Is<string>(s => s.Equals("admin"))),
Times.Exactly(1),
"Must check if user is in role 'admin'");
}
Notice that there is no need to create mock for HttpContext, Moq supports nesting of properties when setting up the test.
When using AspNetCore I could not mock the ControllerContext since I got an exception.
Unsupported expression: m => m.HttpContext
Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.
Instead I had to mock the HttpContext and create a ControllerContext and pass the HttpContext object along.
I did find that mocking claims or response/request objects works as well when using this method.
[Test]
public void TestSomeStuff() {
var name = "some name";
var httpContext = new Mock<HttpContext>();
httpContext.Setup(m => m.User.IsInRole("RoleName")).Returns(true);
httpContext.Setup(m => m.User.FindFirst(ClaimTypes.Name)).Returns(name);
var context = new ControllerContext(new ActionContext(httpContext.Object, new RouteData(), new ControllerActionDescriptor()));
var controller = new MyController()
{
ControllerContext = context
};
var result = controller.Index();
Assert.That(result, Is.Not.Null);
}

Categories