单元测试异步无效(即发即弃)方法

问题描述 投票:0回答:1

我正在 MSTest 中为 WPF 应用程序编写测试,其中包含对 async void 方法(即发即弃模式)的调用。

我的目标是在测试期间同步运行此代码。

这可能吗?

我尝试过“同步”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()

private SynchronousSynchronizationContext _syncContext;

[TestInitialize]
public void TestInitialize()
{    
    _syncContext = new SynchronousSynchronizationContext();
    SynchronizationContext.SetSynchronizationContext(_syncContext);
}

[TestMethod]
public void MyTest_ReturnsData()
{
    // ACT
    MyTest();

    // Finish all fire-and-forget tasks:
    _syncContext.Complete();

    // ASSERT
    ...

}

有没有办法同步运行代码而不需要调用

Complete()

c# unit-testing async-await mstest
1个回答
0
投票

我完全赞成将

async void
转发给
async Task
,但是针对这个问题。

每个测试我们可以有一个

SynchronizationContext
实例,并依靠
async void
状态机调用
OperationCompleted
,我们可以在其中发出方法已完成的信号。我们还获得了异常
Post
,我们可以将其包装并重新抛出到我们将介绍的阻塞
WaitForCompletion
方法中。

void Main() { // or test method

    var syncContext = new AsyncVoidSyncContext();
    // to be instantiated in every test method
    SynchronizationContext.SetSynchronizationContext(syncContext);
    AsyncVoidMethod();
    syncContext.WaitForCompletion(); 

}

async void AsyncVoidMethod() {

    await Task.Delay(3000);
    ThrowException();
    Console.WriteLine("Bar");
}

public void ThrowException() {
    throw new Exception("Foo");
}

public sealed class AsyncVoidSyncContext : SynchronizationContext {

    ManualResetEventSlim _mres = new ManualResetEventSlim(false);
    ExceptionDispatchInfo _exceptionDispatchInfo;

    public void WaitForCompletion() {
        _mres.Wait();
        _exceptionDispatchInfo?.Throw();
    }

    public override void Post(SendOrPostCallback d, object state) {
        try {
            d(state);
        } catch (Exception ex) {
            _exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
        }
    }

    public override void OperationCompleted() {
        _mres.Set();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.