Внедрение зависимостей с помощью XUnit Mediatr и IServiceCollection

В настоящее время я могу обрабатывать IServiceCollection, чтобы вводить имитацию для определенных служб следующим образом.

public class TestClass
{
    private IMediator _mediatr;

    private void SetupProvider(IUnitOfWork unitOfWork, ILogger logger)
    {
        configuration = new ConfigurationBuilder().Build();
        _services = new ServiceCollection();
        _services.AddSingleton(configuration);
        _services.AddScoped(x => unitOfWork);
        _services.AddSingleton(logger);
        _services.AddMediatR(Assembly.Load("Application"));
        _services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
        _mediator = _services.BuildServiceProvider().GetService<IMediator>();
    }

    [Fact]
    public async void UnitTest_Success()
    {
        var unitOfWork = new Mock<IUnitOfWork>();
        var logger = new Mock<ILogger>();
        SetupProvider(unitOfWork.Object, logger.Object);
        var fixture = new Fixture();
        var command = fixture.Create<MediatorCommand>();

        unitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
        .ReturnsAsync(new List<Domain.Model>());

        var response = await _mediatr.Send(command);

        using (new AssertionScope())
        {
            response.Should().NotBeNull();
            response.IsSuccess.Should().BeTrue();
        }
    }
}

Для следующего испытуемого

public class MediatorCommand : IRequest<CommandResponse>
{
    public string Name { get; set ;}
    public string Address { get; set; }
}

public class MediatorCommandHandler : IRequestHandler<MediatorCommand, CommandResponse>
{
    private readonly ILogger _logger;
    private readonly IUnitOfWork _unitOfWork;

    public MediatorCommandHandler(IUnitOfWork unitOfWork, ILogger logger)
    {
        _logger = logger;
        _unitOfWork = unitOfWork;
    }

    public async Task<CommandResponse> Handle(MediatorCommand command, CancellationToken cancellationToken)
    {
        var result = new CommandResponse { IsSuccess = false };
        try
        {
            var entity = GetEntityFromCommand(command);
            await _unitOfWork.Save(entity);
            result.IsSuccess = true;
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, ex.Message);
        }

        return result;
    }
}

Этот тест проходит нормально, и в обработчиках команд используются макеты unitOfWork и logger.

Я пытаюсь переместить это так, чтобы конструкция IServiceCollection выполнялась для каждого класса, а не для каждого теста, используя следующее:

public class SetupFixture : IDisposable
{
    public IServiceCollection _services;
    public IMediator Mediator { get; private set; }
    public Mock<IUnitOfWork> UnitOfWork { get; private set; }

    public SetupFixtureBase()
    {
        UnitOfWork = new Mock<IUnitOfWork>();
        configuration = new ConfigurationBuilder().Build();
        _services = new ServiceCollection();
        _services.AddSingleton(configuration);
        _services.AddScoped(x => UnitOfWork);
        _services.AddSingleton(new Mock<ILogger>().Object);
        _services.AddMediatR(Assembly.Load("Application"));
        _services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
        Mediator = _services.BuildServiceProvider().GetService<IMediator>();
    }

    public void Dispose()
    {
        Mediator = null;
        _services.Clear();
        _services = null;
    }
}

public class TestClass : IClassFixture<SetupFixture>
{
    protected readonly SetupFixture _setupFixture;

    public UnitTestBase(SetupFixture setupFixture)
    {
        _setupFixture = setupFixture;
    }

    [Fact]
    public async void UnitTest_Success()
    {
        var fixture = new Fixture();
        var command = fixture.Create<MediatorCommand>();

        _setupFixture.UnitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
        .ReturnsAsync(new List<Domain.Model>());

        var response = await _mediatr.Send(command);

        using (new AssertionScope())
        {
            response.Should().NotBeNull();
            response.IsSuccess.Should().BeTrue();
        }
    }
}

К сожалению, с этим методом мои макеты не внедряются в обработчик команд. Есть ли способ заставить это работать?

Спасибо,

1 ответ

Я обнаружил проблему, и она не связана с переходом на IClassFixuture<>. Проблема заключалась в том, что я инициализировал Mediator в базовом классе, а затем добавлял имитацию UnitOfWork в производный класс.

Это приводит к сбою инициализации посредника, потому что одно из вариантов поведения ожидало UnitOfWork, которого в то время еще не было в контейнере.

Перемещение инициализации посредника после добавления всех сервисов помогло мне решить проблему, и теперь все работает, как ожидалось.

Если вы попробуете то же самое, не забудьте включить все службы в контейнер перед инициализацией любых объектов, требующих этих зависимостей.

Спасибо всем, кто внес свой вклад.

Другие вопросы по тегам