简体   繁体   English

Rx和异步nunit测试

[英]Rx and async nunit test

I'm trying to create an async unit test for the project, but cannot understand how to wait for the async subject to complete: 我正在尝试为项目创建异步单元测试,但无法理解如何等待异步主题完成:

    [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);
    }

Sync version of this test is works well: 此测试的同步版本运行良好:

    [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 versus Subject AsyncSubject与Subject

First off, it's worth pointing out that AsyncSubject<T> is not an asynchronous version of Subject<T> . 首先,值得指出AsyncSubject<T>不是Subject<T>的异步版本。 Both are in fact free-threaded* (see footnote). 两者实际上都是自由线程*(见脚注)。

AsyncSubject is a specialization of Subject intended to be used to model an operation that completes asynchronously and returns a single result. AsyncSubjectSubject一个特化,旨在用于建模异步完成并返回单个结果的操作。 It has two noteworthy features: 它有两个值得注意的特点:

  • Only the last result is published 仅发布最后一个结果
  • The result is cached and is available to observers subscribing after it has completed. 结果被缓存,并且在完成后订阅的观察者可以使用。

It is used internally in various places, including by the ToObservable() extension method defined on Task and Task<T> . 它在内部用于各种地方,包括在TaskTask<T>上定义的ToObservable()扩展方法。

The issue with the test 测试的问题

Recall AsyncSubject<T> will only return the final result received. Recall AsyncSubject<T>仅返回收到的最终结果。 It does this by waiting for OnCompleted() so it knows what the final result is. 它通过等待OnCompleted()来实现这一点,因此它知道最终结果是什么。 Because you do not call OnCompleted() on first your test is flawed as the OnNext() handler - the lambda function passed in your Subscribe call - will never be invoked. 因为你不叫OnCompleted()first测试是有缺陷的OnNext()处理程序-将永远不会被调用-在你的订阅调用传递lambda函数。

Additionally, it is invalid not to call OnNext() at least once on an AsyncSubject<T> , so when you call await second; 此外,它是无效的调用OnNext()上的至少一次AsyncSubject<T>所以当调用await second; you will get an InvalidOperationException if you haven't done this. 如果你还没有这样做,你将得到一个InvalidOperationException

If you write your test as follows, all is well: 如果你按如下方式编写测试,一切都很好:

[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);
}

About asynchronous tests 关于异步测试

As a general rule I would avoid writing asynchronous tests that could wait forever. 作为一般规则,我会避免编写可以永远等待的异步测试。 This gets particularly annoying when it causes resource drains on build servers. 当它导致构建服务器上的资源耗尽时,这尤其令人讨厌。 Use some kind of timeout eg: 使用某种超时,例如:

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

No need to handle the exception since that is enough for the test to fail. 无需处理异常,因为这足以使测试失败。

**I've borrowed this term from the COM lexicon . **我从COM词典中借用了这个术语。 In this sense I mean that they, as with most of the Rx framework components, will generally run on whatever thread you happen to invoke their methods on. 从这个意义上讲,我的意思是,与大多数Rx框架组件一样,它们通常会在您调用其方法的任何线程上运行。 Being free-threaded doesn't necessarily mean being fully thread safe though. 自由线程并不一定意味着完全线程安全。 In particular, unlike AsyncSubject<T> , Subject<T> doesn't protect you from the Rx grammar violation of making overlapping calls to OnNext . 特别是,与AsyncSubject<T>Subject<T>不会保护您免受Rx语法冲突的影响,即对OnNext进行重叠调用。 Use Subject.Synchronize or Observable.Synchronize for this protection.* 使用Subject.SynchronizeObservable.Synchronize进行此保护。*

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

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