web-dev-qa-db-fra.com

Unité testant un AuthorizeAttribute sur un contrôleur d'API ASP.NET Core MVC

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());
    }
12
Ivan

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.

7
Nkosi

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();
}
2
codeMoh