J'ai une API ASP.NET Core MVC avec des contrôleurs qui doivent être testés à l'unité.
Contrôleur:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace TransitApi.Api.Controllers
{
[Route("api/foo")]
public class FooController : Controller
{
private IFooRepository FooRepository { get; }
public FooController(IFooRepository fooRepository)
{
FooRepository = fooRepository;
}
[HttpGet]
[Authorize("scopes:getfoos")]
public async Task<IActionResult> GetAsync()
{
var foos = await FooRepository.GetAsync();
return Json(foos);
}
}
}
Il est essentiel que je puisse tester unitairement l'efficacité du AuthorizeAttribute
. Nous avons eu des problèmes dans notre base de code avec des attributs manquants et des étendues incorrectes. Cette réponse est exactement ce que je recherche, mais sans méthode ActionInvoker
dans Microsoft.AspNetCore.Mvc.Controller
signifie que je ne suis pas en mesure de le faire de cette façon.
Test unitaire:
[Fact]
public void GetAsync_InvalidScope_ReturnsUnauthorizedResult()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
// User unfortunately not available in HttpContext
//,User = new User() { Scopes = "none" }
}
};
// Act
var result = controller.GetAsync().Result;
// Assert
Assert.IsType<UnauthorizedResult>(result);
}
Comment puis-je tester unitairement que les utilisateurs sans les bonnes étendues se voient refuser l'accès à ma méthode de contrôleur?
Actuellement, je me suis contenté de tester simplement la présence d'un AuthorizeAttribute
comme suit, mais ce n'est vraiment pas assez bon:
[Fact]
public void GetAsync_Analysis_HasAuthorizeAttribute()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
}
};
// Act
var type = controller.GetType();
var methodInfo = type.GetMethod("GetAsync", new Type[] { });
var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
// Assert
Assert.True(attributes.Any());
}
Cela nécessiterait des tests d'intégration avec un serveur de test en mémoire car l'attribut est évalué par le framework lors du traitement du pipeline de requêtes.
Test d'intégration dans ASP.NET Core
Les tests d'intégration garantissent que les composants d'une application fonctionnent correctement lorsqu'ils sont assemblés. ASP.NET Core prend en charge les tests d'intégration à l'aide de cadres de tests unitaires et d'un hôte Web de test intégré qui peut être utilisé pour gérer les demandes sans surcharge réseau.
[Fact]
public async Task GetAsync_InvalidScope_ReturnsUnauthorizedResult() {
// Arrange
var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
var client = server.CreateClient();
var url = "api/foo";
var expected = HttpStatusCode.Unauthorized;
// Act
var response = await client.GetAsync(url);
// Assert
Assert.AreEqual(expected, response.StatusCode);
}
Vous pouvez également créer une start-up spécifiquement pour le test qui remplacera toutes les dépendances pour DI avec des stubs/mocks si vous ne voulez pas que le test atteigne les implémentations de production réelles.
Ce que vous pourriez faire, est de configurer votre serveur de test pour ajouter un middleware de filtre anonyme:
private HttpClient CreatControllerClient()
{
return _factory.WithWebHostBuilder(builder
=> builder.ConfigureTestServices(services =>
{
// allow anonymous access to bypass authorization
services.AddMvc(opt => opt.Filters.Add(new AllowAnonymousFilter()));
})).CreateClient();
}