Moq 在调用 Task.WhenAll 的任务之一中抛出异步异常

Moq Throw async exception in one of tasks in call to Task.WhenAll

我有一些任务要传递给 Task.WhenAll 调用。在我的测试中,我正在设置第一个抛出异常的任务,但我对 Tasks.WhenAll 的调用并未针对所有任务完成,并且在抛出异常时立即中断。我的假设是当调用 Tasks.WhenAll 时所有任务都将完成并返回,所以我认为 Moq 无法区分抛出异步和非异步异常。

我希望能够允许所有任务 运行,一旦所有任务都完成,我想抓取出错的任务并提取它们的异常并采取相应的行动。我的实施是否正确?是 Moq 无法应对还是我必须修复我的实现并且问题不在于 Moq?

public async Task StartAsync(TextWriter log)
{
    log = TextWriter.Synchronized(log);
    var processorTaks = _processors.Select(x => x.StartAsync(log)).ToList();
    Task.WhenAll(processorTaks).Wait();
    var innerExceptions = processorTaks.Where(x => x.Exception != null)
        .Select(x => x.Exception.InnerException)
        .ToList();
    if (innerExceptions.Any())
    {
        innerExceptions.AddRange(_processors.SelectMany(x => x.NotifierException).ToList());
        var formattedExceptions = innerExceptions
            .Select(ex => $"{ex.Message}<\br>{ex.StackTrace}")
            .Distinct();
        IEnumerable<Task> sendEmailTaks = _alertEmailAddresses
            .Split('|')
            .Select(
                x =>
                    _smtpClient.SendMailAsync($"Notifications failed with {innerExceptions.Count()} exceptions",
                        string.Join("<\br>", formattedExceptions), x));
        var retry = _createWebRetry();
        await retry.RetryAsync(() => Task.WhenAll(sendEmailTaks), CancellationToken.None);
    }
}

我的处理器设置:

FirstProcessor.Setup(x => x.StartAsync(It.IsAny<TextWriter>())).Throws(new Exception("some exception happened."));
SecondProcessor.Setup(x => x.StartAsync(It.IsAny<TextWriter>())).Returns(Task.FromResult(default(object)));

Task.WhenAll 会调用 StartAsync,那会抛出。 在调用线程上抛出异常。在创建任务之前。

你想要StartAsync到return一个Task:

firstProcessor.Setup(x => x.StartAsync(It.IsAny<TextWriter>())).Returns(new Task(() => { throw new Exception("err"); }));

正如 Bruno 正确指出的那样,问题在于模拟的 StartAsync 正在同步抛出异常,而不是返回错误的任务。

但是,正确的代码不能使用 new Task(这将导致挂起,因为任务从未启动)。相反,使用 Task.FromException:

FirstProcessor.Setup(x => x.StartAsync(It.IsAny<TextWriter>())).Returns(
    Task.FromException(new Exception("some exception happened."))
);