Общий контекст между тестами - PullRequest
0 голосов
/ 25 декабря 2018

У меня есть следующий код:

public class Batcher<TPayload> : IBatcher<TPayload>
{    
    private static readonly BufferBlock<BatchElement<TPayload>> BufferBlock = new BufferBlock<BatchElement<TPayload>>(new DataflowBlockOptions
    {
        EnsureOrdered = true
    });

    private readonly TransformBlock<BatchElement<TPayload>, BatchElement<TPayload>> BufferInterceptor;
    private readonly TransformBlock<BatchElement<TPayload>, BatchElement<TPayload>> TimeoutInterceptor;

    public EventsBatcher(int size, int interval, IMagicService magicService, ILogger<Batcher<TPayload, TStrategy>> logger)
    {
        BufferInterceptor =
            new TransformBlock<BatchElement<TPayload>, BatchElement<TPayload>>(x =>
            {
                logger.LogInformation($"Get a message with value: {x}");
                return x;
            });

        TimeoutInterceptor =
            new TransformBlock<BatchElement<TPayload>, BatchElement<TPayload>>(x =>
            {
                logger.LogInformation($"Move out from transformation block with a value: {x}");
                return x;
            });

        var batchBlock = new BatchBlock<BatchElement<TPayload>>(size, new GroupingDataflowBlockOptions()
        {
            EnsureOrdered = true
        });

        var timer = new Timer(async _ =>
        {
            try
            {
                batchBlock.TriggerBatch();
                var data = await batchBlock.ReceiveAsync();
                if (!data.Any() && data.SomeLogic())
                     return;

                await magicService.PushMessageAsync(batchElement.Payload);
            }
            catch (Exception e)
            {
                logger.LogError($"Error occurs while trying to invoke action on batch", e);
            }
        }, null, 0, 500);

        var timeoutBlock = new TransformBlock<BatchElement<TPayload>, BatchElement<TPayload>>(v =>
        {
            timer.Change(interval, Timeout.Infinite);
            return v;
        });

        TimeoutInterceptor.LinkTo(batchBlock);
        timeoutBlock.LinkTo(TimeoutInterceptor);
        BufferInterceptor.LinkTo(timeoutBlock);
        BufferBlock.LinkTo(BufferInterceptor);
    }

    public async Task<Result<Unit>> SendAsync(BatchElement<TPayload> msg, CancellationToken token = new CancellationToken())
    {
        try
        {
            var result = await BufferBlock.SendAsync(msg, token);
            return result
                ? ResultFactory.CreateSuccess()
                : ResultFactory.CreateFailure<Unit>("Message was refused by queue");
        }
        catch (Exception e)
        {
            return ResultFactory.CreateFailure<Unit>(e.Message);
        }
    }     
}

Какая ответственность состоит в том, чтобы каким-либо образом оценивать данные каждые x миллисекунд.Я пытаюсь написать модульные тесты, чтобы убедиться, что все работает нормально.Эти тесты здесь:

public class BatcherTests
{
    public EventsBatcher<int> Initialize(Dictionary<DateTime, int> output)
    {
        var busMock = new Mock<IMagicService>();
        busMock.Setup(x => x.PushMessageAsync(It.IsAny<int>()))
            .Callback<Data>((data) =>
            {
                output.Add(DateTime.Now, data);
            }).Returns(Task.CompletedTask);

        var loggerMock = new Mock<ILogger<Batcher<int>>>();
        return new Batcher<int>(
            2, 
            5000, 
            busMock.Object,
            loggerMock.Object
        );
    }

    [Fact]
    public async Task Batcher_ShouldRemoveDuplicatedMessages()
    {
        var output = new Dictionary<DateTime, int>();
        var batcher = Initialize(output);
        var first = await batcher.SendAsync(new MockEvent { Payload = 1 });
        var second = await batcher.SendAsync(new MockEvent { Payload = 1 });

        (first.IsSuccess && second.IsSuccess).ShouldBeTrue();
        while (output.Count != 2)
        {
        }

        output.Count.ShouldBe(2);
        output.First().Value.ShouldBe(1);
        output.Last().Value.ShouldBe(1);
        output.Clear();
    }

    [Fact]
    public async Task Batcher_WhenSizeIsSetTo2AndWeSend3Items_ReturnTwoBatchedItemsWithDateIntervalPlusMinus5000msAndAllSendRequestsEndsWithSuccess()
    {
        var output = new Dictionary<DateTime, int>();
        var batcher = Initialize(output);

        var first = await batcher.SendAsync(new MockEvent { Payload = 1 });
        var second = await batcher.SendAsync(new MockEvent { Payload = 1 });
        var third = await batcher.SendAsync(new MockEvent { Payload = 1 });

        (first.IsSuccess && second.IsSuccess && third.IsSuccess).ShouldBeTrue();
        while (output.Count != 2) //never ends because there are already two elements in output dictionary
        {
        }

        output.Count.ShouldBe(2);
        output.First().Value.ShouldBe(2);
        output.Last().Value.ShouldBe(1);

        var interval = (output.Last().Key - output.First().Key).TotalSeconds;

        (interval >= 4.5d && interval <= 5.5d).ShouldBeTrue();
        output.Clear();
    }
}

Но странно то, что когда я запускаю их отдельно, они заканчиваются статусом успеха.Но когда я запускаю их все вместе, один из них, кажется, застрял.Это потому, что словарь, который передается логическому методу, имеет 2 элемента внутри при запуске теста.Я не вижу здесь возможности общего контекста, так как класс stub создается в начале тестовых случаев, то же самое со словарем.Я что-то упустил?Я также пытаюсь разделить эти контрольные примеры на отдельные классы, но происходит то же самое.

1 Ответ

0 голосов
/ 27 декабря 2018

Существует общий доступ, но его нет в тесте (напрямую).

Ваш BufferBlock объявлен как static в классе Batcher<TPayload>.Это ваше общее состояние.

private static readonly BufferBlock<BatchElement<TPayload>> BufferBlock = new BufferBlock<BatchElement<TPayload>>(new DataflowBlockOptions
{
    EnsureOrdered = true
});

Когда выполняется несколько тестов, этот общий блок несколько раз связывается с другими блоками.

...