c# - Rx 和异步 nunit 测试

标签 c# asynchronous system.reactive

我正在尝试为项目创建异步单元测试,但无法理解如何等待异步主题完成:

    [Test]
    public async void MicroTest()
    {
        var value = 2;

        var first = new AsyncSubject<int>();
        var second = new AsyncSubject<int>();

        first.Subscribe(_ =>
        {
            value = _;
            second.OnCompleted();
        });

        first.OnNext(1);

        // how to wait for the second subject to complete?

        Assert.AreEqual(value, 1);
    }

此测试的同步版本运行良好:

    [Test]
    public void MicroTest()
    {
        var value = 2;

        var first = new Subject<int>();
        var second = new Subject<int>();

        first.Subscribe(_ =>
        {
            value = _;
            second.OnCompleted();
        });

        first.OnNext(1);

        Assert.AreEqual(value, 1);
    }

最佳答案

AsyncSubject 与 Subject

首先,值得指出的是 AsyncSubject<T>不是 Subject<T> 的异步版本.两者实际上都是自由线程*(见脚注)。

AsyncSubjectSubject 的特化旨在用于模拟异步完成并返回单个结果的操作。它有两个值得注意的特点:

  • 只发布最后一个结果
  • 结果被缓存,并在完成后可供订阅的观察者使用。

它在内部的各个地方使用,包括 ToObservable() Task 上定义的扩展方法和 Task<T> .

测试的问题

召回AsyncSubject<T>只会返回收到的最终结果。它通过等待 OnCompleted() 来完成此操作,因此它知道最终结果是什么。因为你不叫OnCompleted()first你的测试有缺陷,因为 OnNext()处理程序 - 在您的 Subscribe 调用中传递的 lambda 函数 - 永远不会被调用。

此外,调用OnNext() 是无效的不是至少一次 AsyncSubject<T> , 所以当你调用 await second;你会得到一个 InvalidOperationException如果您还没有这样做。

如果您按如下方式编写测试,一切都很好:

[Test]
public async void MicroTest()
{
    var value = 2;

    var first = new AsyncSubject<int>();
    var second = new AsyncSubject<int>();

    first.Subscribe(_ =>
    {
        // won't be called until an OnCompleted() has
        // been invoked on first
        value = _;
        // you must send *some* value to second
        second.OnNext(_);
        second.OnCompleted();
    });

    first.OnNext(1);
    // you must do this for OnNext handler to be called
    first.OnCompleted(); 

    // how to wait for the second subject to complete
    await second;

    Assert.AreEqual(value, 1);
}

关于异步测试

作为一般规则,我会避免编写可能永远等待的异步测试。当它导致构建服务器上的资源耗尽时,这会变得特别烦人。使用某种超时,例如:

await second.Timeout(TimeSpan.FromSeconds(1));

不需要处理异常,因为这足以让测试失败。

**我从the COM lexicon借用了这个词.在这个意义上,我的意思是,与大多数 Rx 框架组件一样,它们通常会在您碰巧调用它们的方法的任何线程上运行。自由线程并不一定意味着完全线程安全。特别是,不像 AsyncSubject<T> , Subject<T>不会保护您免受对 OnNext 的重叠调用的 Rx 语法违规.使用 Subject.Synchronize Observable.Synchronize 为了这种保护。*

关于c# - Rx 和异步 nunit 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22634195/

相关文章:

javascript - Node.js 只使用数组中的最后一项

c# - 加入 Rx 流

c# - 使用 Observable.FromEventPattern 在不活动或计数后执行操作

objective-c - Mac OS 命令行工具处理异步导出视频以进行重新编码(已编辑并正在运行)

c# - subject.OnError 抛出

c# - 无法将类型 'System.Data.Entity.DynamicProxies.ApplicationUser_xxxxxxxx' 的对象强制转换为类型 'System.String'

c# - 从变量快速创建 C# 属性

C# 泛型运算符

c# - htmlAttributes 未与我的扩展中的标签生成器合并

javascript - Node - 在 For 周期内等待请求响应