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