And*_*son 2 .net c# wpf unit-testing system.reactive
Reactive Extensions有一个性感的小钩子来简化调用异步方法:
var func = Observable.FromAsyncPattern<InType, OutType>(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
Run Code Online (Sandbox Code Playgroud)
我在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);
}
Run Code Online (Sandbox Code Playgroud)
我看到的问题是,我指定在异步操作完成时运行的代码(在本例中为Foo(x))永远不会被调用.我可以通过在Foo中设置断点并观察它们永远不会到达来验证这一点.此外,我可以在调用DoMethodThatInvokesService(启动异步调用)后强制延迟一段时间,但代码仍然没有运行.我不知道的代码调用的Rx框架的线被称为.
我试过的其他事情:
我试图根据这里的建议修改第二行: Reactive Extensions Rx - 用ObserveOnDispatcher测试一些东西 没有爱.
我已经添加.Take(1)到Rx代码如下:
func(inData).ObserveOnDispatcher().Take(1).Subscribe(x => Foo(x));
这将我的失败率提高到了五分之一,但它们仍然存在.
最后我手边有一个工作(即不要使用Rx),但我觉得它并不理想.如果有人在过去遇到这个问题并找到了解决方案,我非常希望能听到它.
更新:
我还发布了Rx论坛,他们将包括一个即将发布的测试调度程序.一旦可用,这可能是最终的解决方案.
问题是MSTest.exe运行Dispatcher(即Dispatcher.Current!= null),因此ObserveOnDispatcher工作.但是,这个Dispatcher什么都不做!(即将忽略排队的调度程序项)您编写的明确使用Schedule.Dispatcher的任何代码都是不可测试的
我在ReactiveUI中通过暴力解决了这个问题- 这是重要的一点:
https://github.com/reactiveui/ReactiveUI/blob/master/ReactiveUI/RxApp.cs#L99
我们基本上设置了一个定义默认调度程序的全局变量,然后我们尝试检测我们何时进入测试运行器.
然后,在我们实现IObservable的类中,都采用IScheduler参数,其默认值最终将成为全局默认调度程序.我可能做得更好,但这对我有用,并使ViewModel代码再次可测试.