单元测试:在断言之前等待所有线程完成



在一个测试中,我正在创建多个线程,为了测试比赛条件,我需要这些线程同时启动,到目前为止还不错。但是,当我断言该方法执行的次数时,if失败了,因为它没有等到所有线程都完成。

[Test]
public void Test_Racing_Condition()
{
//Arrange & Act
var threads = new Thread[20];
for (int i = 0; i < threads.Length; i++)
{
thread[i] = new Thread(async () => await _memory.GetMemoryAsync());
}
foreach (Thread thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
//Assert
Mock.Assert(() => _memory.GetMemoryAsync(), Occurs.Exactly(20));
}

如何强制测试在断言之前等待所有线程?

我遇到的问题是,在线程完成之前,断言已经完成,因此测试失败。

这里的问题是您在这里使用异步voidnew Thread(async () => await _memory.GetMemoryAsync())。一旦_memory.GetMemoryAsync()等待第一个未完成的等待,方法就会返回,线程就会完成它的工作。

试试这个:

[Test]
public async Task Test_Racing_Condition()
{
//Arrange & Act
var tasks = new Task[20];
for (int i = 0; i < tasks.Length; i++)
{
// Use Task.Run to return immediately.
// Not necessary in production code.
tasks[i] = Task.Run(_memory.GetMemoryAsync);
}
await Task.WhenAll(tasks);
//Assert
Mock.Assert(() => _memory.GetMemoryAsync(), Occurs.Exactly(20));
}

如果你真的需要他们尽可能地从最接近的地方开始:

[Test]
public async Task Test_Racing_Condition()
{
//Arrange
using (var tcs = new TaskCompletionSource<object>())
{
var tasks = new Task[20];
for (int i = 0; i < tasks. Length; i++)
{
// Use Task.Run to return immediately.
// Not necessary in production code.
tasks[i] = Task.Run(async () =>
{
await tcs.Task;
await _memory.GetMemoryAsync();
});
}
// Act
tcs.SetResult(null);
}
await Task.WhenAll(tasks);
//Assert
Mock.Assert(() => _memory.GetMemoryAsync(), Occurs.Exactly(20));
}

通过Nunit测试,我使用DelayedConstraint。DelayedConstraint会延迟另一个约束的应用,直到经过一定的时间。在最简单的形式中,它取代了代码中的Sleep,但它也支持轮询,这可能允许使用更长的最长时间,同时仍然保持测试尽可能快。

DelayedConstraint constraint = Is.True.After(delayInMilliseconds: 100, pollingInterval: 5);
Assert.That(() => _memory.GetMemoryAsync(), Occurs.Exactly(20), expr: constraint);