如何对带有异步的流进行单元测试

Posted

技术标签:

【中文标题】如何对带有异步的流进行单元测试【英文标题】:How to unit test a flow with async down the line 【发布时间】:2021-06-12 08:26:44 【问题描述】:

我有一个以常规函数开始的流程(方法签名中没有异步),然后我调用 Task.Run(await () => asyncFunction())。

除了等待而不期待任何回报是不好的做法之外,有没有一种方法可以运行单元测试来将代码运行到最后? 目前,一旦单元测试完成,需要在 Task.Run 中运行的任务将被取消,因为该任务是异步的并被放在任务队列的末尾(据我所知),主线程完成并且任务没有机会运行。

谢谢

编辑:添加 MVP:

using System;
using System.Threading.Tasks;

namespace SomeNamespace

    public class Class1
    
        public void Foo()
        
            Task.Run(async () => await AsyncMethod());
        

        private async Task AsyncMethod()
        
            await Task.Delay(3000);
            Console.WriteLine("Waited 3 seconds");
        
    

和单元测试:

[TestMethod]
public void Debug2()

    var c = new Class1();
    c.Foo();

我想执行写行

谢谢

【问题讨论】:

异步函数是否改变任何状态?你能检查那个状态吗?在你的测试中等待,直到观察到状态变化(忙着等待,thread.sleep,...) @knittl 这是个好主意,但不幸的是,异步函数执行无效的非状态更改(如纯函数)逻辑。 @Ace66 如果它没有任何副作用(即是纯的)并且您没有观察到它的返回值,那么它没有做任何有用的事情(如果它没有,程序将是等效的'根本不运行)。 @Ace66 我最初的建议仍然有效:阻塞主线程,直到你观察到你的副作用。无论是忙着等待(while (!logFile.contains("expected));),“估计”睡眠(Thread.sleep(5000); Assert.That(logFile…))还是更复杂的事情(例如注入带有等待句柄的自定义记录器)。如果副作用很重要,请使其可观察到。如果无法观察到,请重构您的代码以使其如此。 @Ace66 [cont'd] ...你写自己“除了这是不好的做法”——现在你知道为什么它被认为是不好的做法:它让你的代码很难测试;) 【参考方案1】:

除了等待而不期待任何回报是不好的做法之外,有没有一种方法可以运行单元测试来将代码运行到最后?

其中一个原因“一劳永逸”是一种不好的做法,即调用代码无法知道工作何时完成(或者它是否完成,或者它是否因错误而完成)。

正是这个原因使测试变得如此困难。您需要注入某种模拟来检测工作的任何副作用,并在测试中忙着等待,直到观察到副作用。超时将是必要的,以允许测试失败(而不是挂起),并且超时会使您的测试天生不稳定。这就是为什么“即发即弃”是一种不好的做法的原因之一。

【讨论】:

以上是关于如何对带有异步的流进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何对使用 Axios(或其他异步更新)的 Vue 组件进行单元测试?

对由 NSTimer 异步发送的 NSNotification 进行单元测试

使用 axios、TypeScript 模板和异步函数对 VueJS 进行单元测试

Angular 11:对 HttpInterceptor 进行单元测试 - 异步计时器或 AfterAll 错误

如何使用异步路由进行 Ember 单元测试?

如何对强制展开进行单元测试