如何在MSTest中同步执行WPF的async void(即发即弃)方法测试?
测试WPF中async void即发即弃方法的同步执行方案
我正在为包含async void(即发即弃模式)方法调用的WPF应用编写MSTest测试,目标是在测试时同步运行这部分代码,请问是否可行?
我尝试实现了一个“同步”的SynchronizationContext:
public class SynchronousSynchronizationContext : SynchronizationContext { private readonly ConcurrentQueue<(SendOrPostCallback, object)> _workItems = new ConcurrentQueue<(SendOrPostCallback, object)>(); private readonly AutoResetEvent _workItemsAvailable = new AutoResetEvent(false); public override void Post(SendOrPostCallback d, object state) { Trace.WriteLine("SynchronousSynchronizationContext.Post(), start"); // Execute the callback immediately d(state); Trace.WriteLine("SynchronousSynchronizationContext.Post(), end"); } /* enqueues the work item to be completed later: public override void Post(SendOrPostCallback d, object state) { Trace.WriteLine("SynchronousSynchronizationContext.Post()"); _workItems.Enqueue((d, state)); _workItemsAvailable.Set(); } */ public override void Send(SendOrPostCallback d, object state) { Trace.WriteLine("SynchronousSynchronizationContext.Send()"); d(state); } public void Run() { Trace.WriteLine("SynchronousSynchronizationContext.Run()"); while (_workItems.TryDequeue(out var workItem)) { workItem.Item1(workItem.Item2); } } public void Complete() { Trace.WriteLine("SynchronousSynchronizationContext.Complete()"); while (_workItems.TryDequeue(out var workItem)) { Trace.WriteLine("SynchronousSynchronizationContext, execute callback"); // execute callback. // The workItem is a tuple where Item1 is the callback and Item2 is the state object. workItem.Item1(workItem.Item2); } } }
但这个实现没达到预期,Post()方法没有同步运行回调,目前必须在测试方法里多次显式调用Complete()才能完成所有异步任务,想知道有没有不用手动调用Complete()就能同步运行代码的方法?
可行方案:修改SynchronizationContext实现
问题核心是async void方法中的await会将后续逻辑通过SynchronizationContext.Post调度,你当前的实现没有捕获这部分后续回调的同步执行。可以调整SynchronousSynchronizationContext,让Post方法在执行回调时临时接管当前上下文,确保所有异步后续步骤都同步完成:
public class SynchronousSynchronizationContext : SynchronizationContext { public override void Post(SendOrPostCallback d, object state) { // 保存原上下文,执行后恢复 var originalContext = Current; try { // 将当前上下文设置为自身,保证await后的回调也走这个同步上下文 SetSynchronizationContext(this); // 同步执行回调 d(state); } finally { SetSynchronizationContext(originalContext); } } public override void Send(SendOrPostCallback d, object state) { // Send本身就是同步调用,直接执行 d(state); } // 不再需要Run/Complete方法,所有回调都同步执行 }
测试代码调整
测试初始化逻辑不变,测试方法中无需再调用Complete():
private SynchronousSynchronizationContext _syncContext; [TestInitialize] public void TestInitialize() { _syncContext = new SynchronousSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(_syncContext); } [TestMethod] public void MyTest_ReturnsData() { // ACT MyTest(); // async void方法内的所有异步操作都会同步完成 // ASSERT // 直接进行断言即可,无需等待异步任务 ... }
原理说明
- WPF中的
async void方法在遇到await时,会将后续代码逻辑通过当前SynchronizationContext.Post调度执行 - 修改后的
Post方法在执行回调期间,临时将当前同步上下文替换为自身,确保await后的回调也进入这个Post方法同步执行 - 整个
async void方法的执行流程变为完全同步,不需要手动触发剩余任务
内容的提问来源于stack exchange,提问作者Rye bread
相关产品推荐
相关产品推荐

