Переопределение политики MVC в интеграционных тестах - PullRequest
2 голосов
/ 30 мая 2019

Я нахожусь в процессе добавления интеграционных тестов на работе для приложения MVC. К многим нашим конечным точкам применяются политики, например,

namespace WorkProject
{
  [Route("A/Route")]
  public class WorkController : Controller
  {
    [HttpPost("DoStuff")]
    [Authorize(Policy = "CanDoStuff")]
    public IActionResult DoStuff(){/* */}
  }
}

Для наших интеграционных тестов я переопределил WebApplicationFactory, как это предлагается в документации ASP .NET Core . Моя цель состояла в том, чтобы перегрузить этап аутентификации и обойти политику, создав класс, который позволяет всем сторонам проходить политику авторизации.

namespace WorkApp.Tests
{
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            base.ConfigureWebHost(builder);
            builder.ConfigureServices(services =>
            {
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = "Test Scheme"; // has to match scheme in TestAuthenticationExtensions
                    options.DefaultChallengeScheme = "Test Scheme";
                }).AddTestAuth(o => { });


                services.AddAuthorization(options =>
                {
                    options.AddPolicy("CanDoStuff", policy =>
                        policy.Requirements.Add(new CanDoStuffRequirement()));
                });

             // I've also tried the line below, but neither worked
             // I figured that maybe the services in Startup were added before these
             // and that a replacement was necessary
             // services.AddTransient<IAuthorizationHandler, CanDoStuffActionHandler>();
             services.Replace(ServiceDescriptor.Transient<IAuthorizationHandler, CanDoStuffActionHandler>());
            });
        }
    }

    internal class CanDoStuffActionHandler : AuthorizationHandler<CanDoStuffActionRequirement>
    {
        public CanDoStuffActionHandler()
        {
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CanDoStuffActionRequirement requirement)
        {
            context.Succeed(requirement);

            return Task.CompletedTask;
        }
    }

    internal class CanDoStuffRequirement : IAuthorizationRequirement
    {
    }
}

Первое, что я делаю со службами, это переопределение аутентификации, как предложено здесь (без бит о переопределении Startup, так как это, похоже, не работает для меня). Я склонен полагать, что это переопределение аутентификации работает. Когда я запускаю свои тесты, я получаю HTTP 403 из среды тестирования xUnit. Если я попадаю на маршрут, который я тестирую из PostMan, я получаю HTTP 401. Я также создал класс, который живет в фабрике пользовательских веб-приложений, которая разрешает все запросы для обработчика авторизации CanDoStuff. Я думал, что это позволит провести интеграционные тесты с помощью политики авторизации, но, как указано выше, я получаю HTTP 403. Я знаю, что 403 будет возвращено, если приложение не знает, где находятся определенные файлы. Однако это пост-маршрут, предназначенный исключительно для получения и обработки данных, и этот маршрут не пытается вернуть какие-либо представления, поэтому этот 403, скорее всего, связан с политикой авторизации, которая по какой-то причине не переопределяется.

Я явно что-то делаю не так. Когда я запускаю тест в режиме отладки и устанавливаю точку останова в функции HandleRequirementsAsync, приложение никогда не прерывается. Есть ли другой способ, которым я должен пытаться переопределить политики авторизации?

1 Ответ

1 голос
/ 31 мая 2019

Вот что я сделал.

  1. Замените WebApplicationFactory своим собственным.Обратите внимание, что я все еще добавил запуск своего приложения в качестве параметра шаблона
  2. Создать функцию при запуске, которая переопределяет добавленную мной функцию ConfigureAuthServices .
  3. Сообщите сборщику в *Функция 1013 * для использования моего пользовательского класса запуска.
  4. Переопределить этап аутентификации в функции ConfigureWebHost с помощью builder.ConfigureServices.
  5. Добавить ссылку на сборку на контроллер, конечную точку которого я пытаюсь выполнить.нажмите в конце builder.ConfigureServices в функции ConfigureWebHost.
  6. Напишите свой собственный IAuthorizationHandler для политики, которая разрешает выполнение всех запросов.

Я надеюсь, что у меня естьпроделал достойную работу, объясняя, что я сделал.Если нет, то, надеюсь, приведенный ниже пример кода будет достаточно простым.

YourController.cs

namespace YourApplication
{
  [Route("A/Route")]
  public class WorkController : Controller
  {
    [HttpPost("DoStuff")]
    [Authorize(Policy = "CanDoStuff")]
    public IActionResult DoStuff(){/* */}
  }
}

Test.cs

namespace YourApplication.Tests
{
    public class Tests
        : IClassFixture<CustomWebApplicationFactory<YourApplication.Startup>>
    {
        private readonly CustomWebApplicationFactory<YourApplication.Startup> _factory;

        public Tests(CustomWebApplicationFactory<YourApplication.Startup> factory)
        {
            _factory = factory;
        }

        [Fact]
        public async Task SomeTest()
        {
            var client = _factory.CreateClient();
            var response = await client.PostAsync("/YourEndpoint");
            response.EnsureSuccessStatusCode();

            Assert.Equal(/* whatever your condition is */);
        }
    }
}

CustomWebApplicationFactory.cs

namespace YourApplication.Tests
{
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            base.ConfigureWebHost(builder);
            builder.ConfigureServices(services =>
            {
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = "Test Scheme"; // has to match scheme in TestAuthenticationExtensions
                    options.DefaultChallengeScheme = "Test Scheme";
                }).AddTestAuth(o => { });


                services.AddAuthorization(options =>
                {
                    options.AddPolicy("CanDoStuff", policy =>
                        policy.Requirements.Add(new CanDoStuffRequirement()));
                });

             services.AddMvc().AddApplicationPart(typeof(YourApplication.Controllers.YourController).Assembly);
             services.AddTransient<IAuthorizationHandler, CanDoStuffActionHandler>();
            });
            builder.UseStartup<TestStartup>();
        }
    }

    internal class CanDoStuffActionHandler : AuthorizationHandler<CanDoStuffActionRequirement>
    {
        public CanDoStuffActionHandler()
        {
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CanDoStuffActionRequirement requirement)
        {
            context.Succeed(requirement);

            return Task.CompletedTask;
        }
    }

    internal class CanDoStuffRequirement : IAuthorizationRequirement
    {
    }
}

TestStartup.cs

namespace YourApplication.Tests
{
    public class TestStartup : YourApplication.Startup
    {
        public TestStartup(IConfiguration configuration) : base(configuration)
        {

        }

        protected override void ConfigureAuthServices(IServiceCollection services)
        {
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...