我正在尝试为项目创建一个异步单元测试,但无法理解如何等待异步主题完成:
[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<T>
不是的异步版本Subject<T>
。两者实际上都是自由线程*(请参阅脚注)。
AsyncSubject
是专门Subject
用于建模异步完成的操作并返回单个结果的操作。它具有两个值得注意的功能:
它在内部广泛使用,包括通过和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));
无需处理异常,因为这足以使测试失败。
**我已从COM词典中借用了这个词。从这个意义上讲,我的意思是,与大多数Rx框架组件一样,它们通常将在您碰巧调用其方法的任何线程上运行。但是,使用自由线程并不一定意味着完全线程安全。特别是,与有所不同的是AsyncSubject<T>
,Subject<T>
它不会保护您免受Rx语法对重复调用的侵犯OnNext
。使用Subject.Synchronize
或Observable.Synchronize
进行此保护。*
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句