繁体   English   中英

Rx和异步nunit测试

[英]Rx and async nunit test

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

    [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一个特化,旨在用于建模异步完成并返回单个结果的操作。 它有两个值得注意的特点:

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

它在内部用于各种地方,包括在TaskTask<T>上定义的ToObservable()扩展方法。

测试的问题

Recall AsyncSubject<T>仅返回收到的最终结果。 它通过等待OnCompleted()来实现这一点,因此它知道最终结果是什么。 因为你不叫OnCompleted()first测试是有缺陷的OnNext()处理程序-将永远不会被调用-在你的订阅调用传递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));

无需处理异常,因为这足以使测试失败。

**我从COM词典中借用了这个术语。 从这个意义上讲,我的意思是,与大多数Rx框架组件一样,它们通常会在您调用其方法的任何线程上运行。 自由线程并不一定意味着完全线程安全。 特别是,与AsyncSubject<T>Subject<T>不会保护您免受Rx语法冲突的影响,即对OnNext进行重叠调用。 使用Subject.SynchronizeObservable.Synchronize进行此保护。*

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM