我确实在下面的代码中遇到了(恕我直言)意外的异常
InvalidOperationException
“序列不包含元素”,我认为情况不应该如此。请参阅下面的代码、背景信息和说明。
问题
为了轻松重现并演示它,使用我的 SUT
Effect
的单元测试中的代码被注释掉,并替换为给定的代码,这应该清楚地表明我正在尝试做什么以及我是什么正在尝试测试。
因此,我可以将问题简化为“当它是一个完全有效的用例时,为什么在执行
Observable.Do
时会出现“序列不包含元素”异常”:
IObservable<int> observable = Observable.Empty<int>();
而且,我想测试这个,一个完成的空序列。
单元测试
public class EffectTests {
[Fact]
public async void TestEmpty() {
// IObservable<int> observable = Effect<Unit, int>.Empty.Invoke()
// .Delay(System.TimeSpan.FromSeconds(1.2));
IObservable<int> observable = Observable.Empty<int>()
.Delay(System.TimeSpan.FromSeconds(1.2));
// Exception has occurred: CLR/System.InvalidOperationException
// Exception thrown: 'System.InvalidOperationException' in System.Reactive.dll: 'Sequence contains no elements.'
// at System.Reactive.Subjects.AsyncSubject`1.GetResult()
// at EffectTests.<TestEmpty>d__0.MoveNext() in
await observable.Do(
onNext: _ => AssertEx.Fail()
);
}
}
public static class AssertEx {
public static void Fail(string message = "")
=> throw new Xunit.Sdk.XunitException(message);
}
背景
我希望注释中的代码和 xUnit 测试设置能够显示我正在尝试做的事情:
我有一个自定义结构体
Effect
,它基本上存储一个返回IObservable<T>
的函数。这应该已经表明了一个事实,即 Effect 实际上可以返回任何类型的可观察值(通过函数 Invoke()
)。该可观察对象现在可能返回将同步或异步发出的值,它可能立即失败或成功,或者它可能发出一个或多个值,或者根本不发出。
在我的单元测试中,在
Effect.Empty
的情况下,Observable 实际上是一个空序列,因此它不会发出任何值。因此,当回调 Assert.Fail()
被调用时,我精确地添加了 onNext
。
附加信息
所以,
Observable.Do
的文档说:
// Summary:
// Invokes an action for each element in the observable sequence, and propagates
// all observer messages through the result sequence. This method can be used for
// debugging, logging, etc. of query behavior by intercepting the message stream
// to run arbitrary actions for messages on the pipeline.
它显然没有说明如果序列为空就会抛出异常。它还清楚地说明了用例,用于“调试、日志记录等”,所以我认为这非常适合我的用例,即单元测试。
我的问题
Observable.Do
会抛出异常?SO 上已经有很多关于“序列不包含元素”的问题,但据我所知,没有人具体提及
Observable.Do
。但任何现有的答案也值得赞赏。
解决方案:
您可以使用
.DefaultIfEmpty()
来避免等待空观察者。
对于你的情况:
[Fact]
public async void TestEmpty() {
// IObservable<int> observable = Effect<Unit, int>.Empty.Invoke()
// .Delay(System.TimeSpan.FromSeconds(1.2));
IObservable<int> observable = Observable.Empty<int>()
.Delay(System.TimeSpan.FromSeconds(1.2));
// Exception has occurred: CLR/System.InvalidOperationException
// Exception thrown: 'System.InvalidOperationException' in System.Reactive.dll: 'Sequence contains no elements.'
// at System.Reactive.Subjects.AsyncSubject`1.GetResult()
// at EffectTests.<TestEmpty>d__0.MoveNext() in
await observable.Do(
onNext: _ => AssertEx.Fail()
).DefaultIfEmpty();
}
更多信息:
问题不在于
Observable.Do
,而在于最终等待空观察者的测试。
和你一样,我没有找到任何文档来说明为什么它会这样。