You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:25:01