单元测试FromAsyncPattern [英] Unit testing with FromAsyncPattern

查看:194
本文介绍了单元测试FromAsyncPattern的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在无扩展有一个性感的小钩,以简化异步调用方法:

The Reactive Extensions have a sexy little hook to simplify calling async methods:

var func = Observable.FromAsyncPattern<InType, OutType>(
    myWcfService.BeginDoStuff,
    myWcfService.EndDoStuff);

func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));

我在WPF项目中使用这一点,它在运行时的伟大工程。

I am using this in an WPF project, and it works great at runtime.

不幸的是,试图使用这种技术我遇到随机故障单元测试方法的时候。 〜3一个测试每五执行包含此code失败。

Unfortunately, when trying to unit test methods that use this technique I am experiencing random failures. ~3 out of every five executions of a test that contain this code fails.

下面是一个简单的测试(使用犀牛/统一自动嘲讽容器实现):

Here is a sample test (implemented using a Rhino/unity auto-mocking container):

[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);
}

这是我看到的问题是,我指定的运行时,异步操作完成code(在这种情况下,美孚(X)),永远不会被调用。我可以通过设置断点美孚和观察,他们从来没有达到过验证这一点。此外,我可以强制长时间的延迟调用DoMethodThatInvokesService(揭开序幕的异步调用)后,和code仍然是永远不会运行。 我做知道,code调用接收框架的线条被称为。

The problem that I see is that the code that I specified to run when the async action completed (in this case, Foo(x)), is never called. I can verify this by setting breakpoints in Foo and observing that they are never reached. Further, I can force a long delay after calling DoMethodThatInvokesService (which kicks off the async call), and the code is still never run. I do know that the lines of code invoking the Rx framework were called.

我试过其他的事情:

  • 我试图按照这里的建议进行修改倒数第二行:<一href="http://stackoverflow.com/questions/2184260/reactive-extensions-rx-unit-testing-something-with-observeondispatcher">Reactive扩展接收 - 单元测试的东西ObserveOnDispatcher 的没有爱情

我已经加入。取(1)到Rx code如下:

I have added .Take(1) to the Rx code as follows:

FUNC(INDATA).ObserveOnDispatcher()采取(1).Subscribe(X =>美孚(X));

func(inData).ObserveOnDispatcher().Take(1).Subscribe(x => Foo(x));

这提高了我的失败率要像1:5,但他们还是发生了。

This improved my failure rate to something like 1 in 5, but they still occurred.

  • 在我改写了接收code使用素雅异步模式。这工作,但我的开发人员的自我真的喜欢使用,而不是无聊的老开始/结束接收。

在最后,我做在手周围有一个工作(即不使用接收),但我觉得这是不理想的。如果有人跑进在过去的这个问题,并找到了解决方法,我特别喜欢听到这种说法。

In the end I do have a work around in hand (i.e. don't use Rx), however I feel that it is not ideal. If anyone has ran into this problem in the past and found a solution, I'd dearly love to hear it.

更新

我也贴在Rx论坛的,它们将被包括在测试调度与下一版本。这将可能是最终的解决办法,一旦它可用。

I also posted on the Rx forums, and they will be including a test scheduler with a coming release. That will probably be the ultimate solution once it is available.

推荐答案

该问题是由计划于 ObserveOnDispatcher 呼叫的异步性。你不能保证他们都被测试完成的时间内完成。所以,你需要把你的调度控制之下。

The problem is caused by the asynchronous nature of the calls scheduled by ObserveOnDispatcher. You can't guarantee that they are all completed by the time your test finishes. So you need to bring the scheduling under your control.

如何注入调度到您的课吗?

How about injecting the scheduler into your class?

然后,而不是调用 ObserveOnDispatcher ,你叫 ObserveOn ,传入的 IScheduler 实施了注射。

Then, rather than calling ObserveOnDispatcher, you call ObserveOn, passing in the the IScheduler implementation that was injected.

在运行时,你会注入 DispatcherScheduler ,但在你的测试,你会注入一个假的调度程序,排队一切,它被赋予的操作和运行这些通过您的测试时间控制。

At run-time you would inject the DispatcherScheduler, but in your tests you would inject a fake scheduler that queues up all the actions that it is given and runs them at a time controlled by your tests.

如果你不喜欢,你正在使用接收调度处处注入的想法,如何创建自己的扩展方法,像这样(未经测试code未来):

If you don't like the idea of having to inject a scheduler everywhere that you're using Rx, how about creating your own extension method, something like this (untested code ahead):

public static MyObservableExtensions
{
   public static IScheduler UISafeScheduler {get;set;}

   public static IObservable<TSource> ObserveOnUISafeScheduler(this IObservable<TSource> source)
   {
       if (UISafeScheduler == null) 
       {
          throw new InvalidOperation("UISafeScheduler has not been initialised");
       }

       return source.ObserveOn(UISafeScheduler);
   }
}

然后在运行时,初始化UISafeScheduler与DispatcherScheduler,并在你的测试,intialise它与你的假调度。

Then at run-time, initialise UISafeScheduler with DispatcherScheduler, and in your tests, intialise it with your fake scheduler.

这篇关于单元测试FromAsyncPattern的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆