I'm trying to add FakeItEasy-based unit tests to a REST API controller of an ASP.NET core app. The public controller methods I need to test call the protected authorization methods implemented in the parent class that rely on the runtime data not available in unit tests. What is the best way to bypass the explicit authorization calls from unit tests? In the other words, how do I make the protected base class method to always succeed?
One option would be to implement authorization calls as a separate interface, but this would require changing the application design, and I would like to make unit tests work without making major changes for now.
Here is the outline of relevant code.
Base controller class:
[ApiController]
public abstract class MicroserviceController: Microsoft.AspNetCore.Mvc.ControllerBase
{
protected readonly ICorrelationContextAccessor _correlation;
protected readonly IConfiguration _config;
protected readonly ILogger _logger;
protected MicroserviceController
(
IConfiguration config,
ILogger logger,
ICorrelationContextAccessor correlation
)
{
_config = config;
_logger = logger;
_correlation = correlation;
}
virtual protected Authorize(string[] scopes, string[] roles)
{
// Authorization logic that relies on the ControllerBase methods and properties.
}
// More methods.
}
Controller class:
[ApiController]
public class UsersController: MicroserviceController
{
private IUserService _userService;
public UsersController
(
IConfiguration config,
ILogger<UsersController> logger,
ICorrelationContextAccessor correlation,
IUserService userService
)
: base(config, logger, correlation)
{
_userService = userService;
}
[HttpGet]
public ActionResult<User> GetUser
(
string userId
)
{
try
{
Authorize(new string[] { "user_read", "user_write", "user_delete" }, null);
}
catch (UnauthorizedException ex)
{
return Unauthorized(ProcessError(ex));
}
catch (Exception ex)
{
return BadRequest(ProcessError(ex));
}
User user;
try
{
user = _userService.GetUserById(userId);
}
catch (UnauthorizedException ex)
{
return Unauthorized(ProcessError(ex));
}
catch (NotFoundException ex)
{
return NotFound(ProcessError(ex));
}
catch (Exception ex)
{
return BadRequest(ProcessError(ex));
}
return Ok(user);
}
Unit test class:
public class UsersControllerTests
{
private IConfiguration _config;
private ILogger<UsersController> _logger;
private ICorrelationContextAccessor _correlation;
private IUserService _userService;
public UsersControllerTests()
{
_config = A.Fake<IConfiguration>();
_logger = A.Fake<ILogger<UsersController>>();
_correlation = A.Fake<ICorrelationContextAccessor>();
_userService = A.Fake<IUserService>();
}
[Fact]
public void UsersController_GetUser_ReturnOk()
{
// Arrange
UsersController usersController = new UsersController(_config, _logger, _correlation, _userService);
string userId = "123456789";
User user = A.Fake<User>();
A.CallTo(() => _userService.GetUserById(userId)).Returns(user);
// HOW DO I FORCE the usersController.Authorize(scopes, roles) CALL TO DO NOTHING?
// Act
ActionResult<User> result = usersController.GetUser(userId);
// Assert
result.Should().NotBeNull();
}
}
Is it possible to suppress the Authorize
calls made by the GetUser
call from the usersController
object? Or should I start working on the application redesign?