Проверка сборки / теста для отсутствующих реализаций запроса / команд в MediatR - PullRequest
0 голосов
/ 24 января 2020

Мы активно используем MediatR в нашем приложении LoB, где мы используем шаблон команды и запроса. Часто, чтобы продолжить разработку, мы сначала выполняем команды и запросы, поскольку они являются простыми POCO.

Иногда это может привести к тому, что вы забудете создать настоящий обработчик команд / обработчик запросов. Так как нет никакой проверки во время компиляции, если на самом деле есть реализация для запроса / команды, мне было интересно, какой будет лучший подход, чтобы увидеть, есть ли реализация, и, если нет, выдать ошибку, прежде чем я смогу слиться с master.

Моя идея на данный момент: создать два теста, один для запросов и один для команд, которые сканируют все сборки на предмет реализации IRequest<TResponse>, а затем сканируют сборки на наличие связанной реализации IRequestHandler<TRequest, TResponse>

Но это все равно потребовало бы сначала выполнить тесты (что происходит в конвейере сборки), что по-прежнему зависит от разработчика, выполняющего тесты вручную (или от настройки VS для выполнения после компиляции).

Я не знаю, есть ли для этого решение во время компиляции, и даже если это будет хорошей идеей?

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Если вы используете. Net Ядро, вы можете использовать Microsoft.AspNetCore.TestHost для создания конечной точки, к которой могут обращаться ваши тесты. Работает примерно так:

var builder = WebHost.CreateDefaultBuilder()
                .UseStartup<TStartup>()
                .UseEnvironment(EnvironmentName.Development)
                .ConfigureTestServices(
                    services =>
                    {
                        services.AddTransient((a) => this.SomeMockService.Object);
                    });

            this.Server = new TestServer(builder);
            this.Services = this.Server.Host.Services;
            this.Client = this.Server.CreateClient();
            this.Client.BaseAddress = new Uri("http://localhost");

Таким образом, мы высмеиваем любые вызовы http (или любые другие вещи, которые мы хотим), но вызывается real .

И наш тесты будут выглядеть следующим образом:

public SomeControllerTests(TestServerFixture<Startup> testServerFixture)
        : base(testServerFixture)
        {
        }

        [Fact]
        public async Task SomeController_Returns_Titles_OK()
        {
            var response = await this.GetAsync("/somedata/titles");

            response.StatusCode.Should().Be(HttpStatusCode.OK);
            var responseAsString = await response.Content.ReadAsStringAsync();
            var actualResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<IEnumerable<string>>(responseAsString);

            actualResponse.Should().NotBeNullOrEmpty();
            actualResponse.Should().HaveCount(20);
        }

Поэтому, когда этот тест выполняется, если вы не зарегистрировали свой обработчик (и), он потерпит неудачу! Мы используем это для подтверждения того, что нам нужно (добавлены записи в БД, отклик на то, что мы ожидаем и т. Д. c), но это хороший побочный эффект, который забывает зарегистрировать ваш обработчик на этапе тестирования!

https://fullstackmark.com/post/20/painless-integration-testing-with-aspnet-core-web-api

0 голосов
/ 06 февраля 2020

Мы прошли проверку (и, следовательно, время сборки); Делимся кодом здесь для фактического теста, который мы проводим один раз для проекта домена. Модули-посредники содержат наши регистрации запросов / команд (обработчиков), модули инфраструктуры содержат наши обработчики запросов;

public class MissingHandlersTests
{
    [Fact]
    public void Missing_Handlers()
    {
        List<Assembly> assemblies = new List<Assembly>();

        assemblies.Add(typeof(MediatorModules).Assembly);
        assemblies.Add(typeof(InfrastructureModule).Assembly);

        var missingTypes = MissingHandlersHelpers.FindUnmatchedRequests(assemblies);

        Assert.Empty(missingTypes);
    }
}

Вспомогательный класс;

public class MissingHandlersHelpers
{      
    public static IEnumerable<Type> FindUnmatchedRequests(List<Assembly> assemblies)
    {
        var requests = assemblies.SelectMany(x => x.GetTypes())
            .Where(t => t.IsClass && t.IsClosedTypeOf(typeof(IRequest<>)))
            .ToList();

        var handlerInterfaces = assemblies.SelectMany(x => x.GetTypes())
            .Where(t => t.IsClass && (t.IsClosedTypeOf(typeof(IRequestHandler<>)) || t.IsClosedTypeOf(typeof(IRequestHandler<,>))))
            .SelectMany(t => t.GetInterfaces())
            .ToList();

        List<Type> missingRegistrations = new List<Type>();
        foreach(var request in requests)
        {
            var args = request.GetInterfaces().Single(i => i.IsClosedTypeOf(typeof(IRequest<>)) && i.GetGenericArguments().Any() && !i.IsClosedTypeOf(typeof(ICacheableRequest<>))).GetGenericArguments().First();

            var handler = typeof(IRequestHandler<,>).MakeGenericType(request, args);

            if (handler == null || !handlerInterfaces.Any(x => x == handler))
                missingRegistrations.Add(handler);

        }
        return missingRegistrations;
    }
}
...