连续进行多个多线程测试时,测试运行停止

时间:2020-07-06 11:36:33

标签: c# multithreading visual-studio unit-testing test-runner

我有一个带有静态ConcurrentQueue的类。一个类接收消息并将其放入队列,而该类上的另一个线程则从该队列读取消息并一次处理它们。该方法将被取消,并带有取消令牌。

清空队列的方法如下:

public async Task HandleEventsFromQueueAsync(CancellationToken ct, int pollDelay = 25)
{
    while (true)
    {
        if (ct.IsCancellationRequested)
        {
            return;
        }

        if(messageQueue.TryDequeue(out ConsumeContext newMessage))
        {
            handler.Handle(newMessage);
        }

        try
        {
            await Task.Delay(pollDelay, ct).ConfigureAwait(true);
        } 
        catch (TaskCanceledException)
        {
            return;
        }
    }
}

我的测试方法如下:

CancellationToken ct = source.Token;
Thread thread = new Thread(async () => await sut.HandleEventsFromQueueAsync(ct));
thread.Start();

EventListener.messageQueue.Enqueue(message1);
EventListener.messageQueue.Enqueue(message2);
await Task.Delay(1000);
source.Cancel(false);

mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2));

因此,我使用新的取消令牌在其自己的线程中启动出队方法。然后,我排队了几条消息,给进程第二秒钟处理它们,然后使用source.Cancel(false)终止线程并使方法返回。然后,我检查处理程序是否被调用了正确的次数。当然,我会进行多种测试,中止出队方法的消息类型和时间不同。

问题在于,当我分别运行任何测试时,它们都将成功。但是,当我尝试将它们作为一个组运行时,Visual Studio不会运行每个测试。没有错误消息,并且确实可以成功进行测试,但是运行仅在第二次测试后停止。

visual studio test explorer

我不知道为什么会这样。我的测试在结构上都是相同的。我每次都正确地中止出队线程。

什么可以迫使Visual Studio停止测试运​​行而又不会引发任何类型的错误?

2 个答案:

答案 0 :(得分:1)

我已经解决了自己的问题。事实证明,新创建的线程引发了异常,当线程引发异常时,这些异常将被忽略,但它们仍然阻止单元测试的进行。解决导致异常的问题后,测试可以正常进行。

答案 1 :(得分:1)

您正在将异步lambda传递给Uncaught TypeError: Cannot read property 'cod3nFilter' of undefined thrown Uncaught TypeError: Cannot read property '0' of undefined thrown Uncaught TypeError: Cannot read property 'length' of undefined thrown 构造函数。 Thread构造函数无法理解异步委托(不接受Thread参数),因此最终得到Func<Task> lambda。异步void方法should be avoided用于不是事件处理程序的任何事情。您遇到的情况是,当代码到达第一个async void时,显式创建的线程终止,其余主体在await线程中运行。似乎代码永远不会因异常而失败,否则进程将崩溃(这是异步void方法的默认行为)。

建议:

  1. 使用ThreadPool代替Task。这样一来,您在退出测试之前将需要Thread做些事情。
await
  1. 考虑使用BlockingCollection或类似Channel的异步队列代替CancellationToken ct = source.Token; Task consumerTask = Task.Run(() => sut.HandleEventsFromQueueAsync(ct)); EventListener.messageQueue.Enqueue(message1); EventListener.messageQueue.Enqueue(message2); await Task.Delay(1000); source.Cancel(false); await consumerTask; // Wait the task to complete mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2)); 。轮询是一种笨拙且效率低下的技术。使用阻塞或异步队列,您将不必执行循环来等待新消息到达。您将能够进入ConcurrentQueue状态,并在收到新消息时立即得到通知。

  2. 使用waiting配置等待。 ConfigureAwait(true)是默认设置,不执行任何操作。

  3. 考虑通过抛出ConfigureAwait(false)来传播取消。这是.NET中传播取消的standard way。所以代替:

OperationCanceledException

...最好这样做:

if (ct.IsCancellationRequested) return;