Rx and asynchronous nunit test

I am trying to create an async unit test for a project, but cannot figure out how to wait for async 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); } 

The synchronous version of this test 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); } 
+7
c # asynchronous system.reactive
source share
1 answer

AsyncSubject and Object

Firstly, it is worth noting that AsyncSubject<T> not an asynchronous version of Subject<T> . Both are virtually free * (see Footnote).

AsyncSubject is a Subject specialization designed to model an operation that completes asynchronously and returns a single result. It has two notable features:

  • Only the latest result is published.
  • The result is cached and available to subscribers who subscribe after its completion.

It is used inside different places, including the ToObservable() extension method defined on Task and Task<T> .

Test problem

Recall that AsyncSubject<T> only returns the final result. He does this by waiting for OnCompleted () to let him know what the end result is. Since you do not call OnCompleted() on first , your test is corrupted as an OnNext() handler - the lambda function passed in your Subscribe call will never be called.

Also, it is not allowed to call OnNext() at least once on AsyncSubject<T> , so when you call await second; , you will get an InvalidOperationException if you haven’t.

If you write your test as follows, everything will be fine:

 [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 rule, I would avoid writing asynchronous tests that could wait forever. This becomes particularly annoying when it leaks resources on build servers. Use some timeout, for example:

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

You do not need to handle the exception, as this is enough to complete the test.

** I took this term from the vocabulary of COM . In this sense, I mean that they, like most components of the Rx infrastructure, will usually be executed on the thread that you call to use them. Being loose thread does not necessarily mean being completely thread safe, though. In particular, unlike AsyncSubject<T> , Subject<T> does not protect you from breaking Rx grammar by making overlapping calls to OnNext . Use Subject.Synchronize or Observable.Synchronize for this protection. *

+12
source share

All Articles