反应式扩展有一个性感的小钩子来简化调用异步方法:
var func = Observable.FromAsyncPattern<InType, OutType>(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
我在 WPF 项目中使用它,它在运行时效果很好。
不幸的是,在尝试对使用这种技术的方法进行单元测试时,我遇到了随机失败。包含此代码的测试的每五次执行中约有 3 次失败。
这是一个示例测试(使用 Rhino/unity 自动模拟容器实现):
[TestMethod()]
public void SomeTest()
{
// arrange
var container = GetAutoMockingContainer();
container.Resolve<IMyWcfServiceClient>()
.Expect(x => x.BeginDoStuff(null, null, null))
.IgnoreArguments()
.Do(
new Func<Specification, AsyncCallback, object, IAsyncResult>((inData, asyncCallback, state) =>
{
return new CompletedAsyncResult(asyncCallback, state);
}));
container.Resolve<IRepositoryServiceClient>()
.Expect(x => x.EndDoStuff(null))
.IgnoreArguments()
.Do(
new Func<IAsyncResult, OutData>((ar) =>
{
return someMockData;
}));
// act
var target = CreateTestSubject(container);
target.DoMethodThatInvokesService();
// Run the dispatcher for everything over background priority
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
// assert
Assert.IsTrue(my operation ran as expected);
}
我看到的问题是,我指定在异步操作完成时运行的代码(在本例中为 Foo(x))从未被调用。我可以通过在 Foo 中设置断点并观察它们永远不会到达来验证这一点。此外,我可以在调用 DoMethodThatInvokesService (启动异步调用)后强制长时间延迟,并且代码仍然永远不会运行。我知道调用 Rx 框架的代码行被调用了。
我尝试过的其他事情:
我试图根据这里的建议修改倒数第二行: Reactive Extensions Rx - unit testing something with ObserveOnDispatcher No love。
我已添加
.Take(1)
到 Rx 代码中,如下所示:func(inData).ObserveOnDispatcher().Take(1).Subscribe(x => Foo(x));
这将我的失败率提高到五分之一,但它们仍然发生。
- 我已经重写了 Rx 代码以使用普通的 jane Async 模式。这行得通,但是我的开发者自我真的很想使用 Rx 而不是无聊的旧开始/结束。
最后,我确实有一个解决方法(即不要使用 Rx),但是我觉得它并不理想。如果有人过去遇到过这个问题并找到了解决方案,我非常乐意听到它。
更新:
我还在Rx 论坛上发帖,他们将在即将发布的版本中包含一个测试调度程序。一旦可用,这可能是最终的解决方案。