如何在需要时测试实现是同步的还是异步的?

本文关键字:同步 异步 实现 测试 | 更新日期: 2023-09-27 18:18:24

我正在尝试围绕自定义SynchronizationContext实现编写单元测试。

该类的两个重要操作是SendPost,其中Send同步调用委托,Post异步调用委托。

我想编写单元测试来验证此行为,即委托是同步执行还是异步执行。我不希望测试依赖于成功案例的延迟,因为它在执行中人为地延长了测试运行时间(但是让失败导致延迟是合理的)。

最初我考虑使用Tasks来指示委托的执行:

var tcs = new TaskCompletionSource<object>();
var context = new CustomSynchronizationContext();
context.Send((state) => tcs.SetResult(null), null);
// Task should already be completed!
Assert.IsTrue(this.tcs.Task.IsCompleted);

然而,这并不能确保在测试运行器继续之前,委托没有被异步执行

我如何安排上下文周围的测试,以确保Send块完成委托和Post没有,但委托都被调用?

如何在需要时测试实现是同步的还是异步的?

我相信您可以使用一对ManualResetEvents来实现这一点。使用下面的代码,只有在测试失败时才会出现减速(这个数字相当高,可以安全地减少)。这里的思想是,我们断言只有当我们阻塞或不阻塞时才会发生的事情必须发生的顺序。

同步测试:

var incall = new ManualResetEvent(false);
var unblock = new ManualResetEvent(false);
var context = new CustomSynchronizationContext();
var t = Task.Run(() => context.Send(state =>
{
    incall.Set();
    unblock.WaitOne(5000);
}, null));
Assert.IsTrue(incall.WaitOne(1000));
Assert.IsFalse(t.Wait(10));
unblock.Set();
Assert.IsTrue(t.Wait(1000));

异步测试:

var incall = new ManualResetEvent(false);
var unblock = new ManualResetEvent(false);
var context = new CustomSynchronizationContext();
var t = Task.Run(() =>context.Post(state =>
{
    incall.Set();
    unblock.WaitOne(5000);
}, null));
Assert.IsTrue(incall.WaitOne(1000));
Assert.IsTrue(t.Wait(1000)); //This will timeout if unblock is blocking completion of the task
unblock.Set();

结合我的想法:

var mainThreadId = Thread.ManagedThreadId;
var sendThreadId;
context.Send((state) => sendThreadId = Thread.ManagedThreadId);
Assert.AreEqual(mainThreadId, sendThreadId);