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

如何在断言Mock任务前等待ContinueWith执行完成?

问题:异步延续任务执行晚于断言导致测试失败

被测代码

public class ClassUnderTest {
    readonly IMyInterface dependency;
    public ClassUnderTest(IMyInterface dependency)
    {
        this.dependency = dependency;
    }

    public bool didFault = false;

    public void MethodUnderTest()
    {
        var task = dependency.DoSomethingAsync();

        if (task != null)
        {
            task.ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    didFault = true;
                }
            });
         }
     }
}

测试代码

[TestCase(TestName = "My test")]
public void MyTestCase()
{
    Mock<IMyInterface> mock = MockRepository.Create<IMyInterface>();
    ClassUnderTest cut = new ClassUnderTest(mock.Object);
    
    mock.Setup(m => m.DoSomethingAsync()).ThrowsAsync(new Exception());
    cut.MethodUnderTest();

    Assert.True(cut.didFault);
}

问题核心是MethodUnderTest中通过ContinueWith注册的延续任务,会在DoSomethingAsync返回的任务完成后异步执行,但测试代码调用MethodUnderTest后立刻执行断言,此时延续任务还未运行,导致断言失败。


解决方案

方案1:改造被测方法为异步方法(推荐)

将MethodUnderTest改为异步方法,用await替代ContinueWith,代码更易读的同时,也能直接在测试中等待任务完成:

修改后的被测代码

public class ClassUnderTest {
    readonly IMyInterface dependency;
    public ClassUnderTest(IMyInterface dependency)
    {
        this.dependency = dependency;
    }

    public bool didFault = false;

    public async Task MethodUnderTestAsync()
    {
        try
        {
            var task = dependency.DoSomethingAsync();
            if (task != null)
            {
                await task;
            }
        }
        catch
        {
            didFault = true;
        }
    }
}

修改后的测试代码

[TestCase(TestName = "My test")]
public async Task MyTestCase()
{
    Mock<IMyInterface> mock = MockRepository.Create<IMyInterface>();
    ClassUnderTest cut = new ClassUnderTest(mock.Object);
    
    mock.Setup(m => m.DoSomethingAsync()).ThrowsAsync(new Exception());
    await cut.MethodUnderTestAsync();

    Assert.True(cut.didFault);
}

方案2:不修改被测代码,在测试中等待延续任务完成

如果无法修改原类代码,可以通过控制任务执行流程,等待延续任务完成后再断言:

[TestCase(TestName = "My test")]
public void MyTestCase()
{
    // 创建一个可控的故障任务
    var taskSource = new TaskCompletionSource<object>();
    taskSource.SetException(new Exception());
    var faultedTask = taskSource.Task;

    Mock<IMyInterface> mock = MockRepository.Create<IMyInterface>();
    mock.Setup(m => m.DoSomethingAsync()).Returns(faultedTask);

    ClassUnderTest cut = new ClassUnderTest(mock.Object);
    cut.MethodUnderTest();

    // 先等待原始故障任务完成
    faultedTask.Wait();
    // 循环检查状态+短延迟,带超时避免无限等待
    var timeout = TimeSpan.FromSeconds(1);
    var start = DateTime.Now;
    while (!cut.didFault && DateTime.Now - start < timeout)
    {
        Task.Delay(10).Wait();
    }

    Assert.True(cut.didFault);
}

内容的提问来源于stack exchange,提问作者T'n'E

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 13:12:45