长期协调取消

我正在为我的应用程序的“粘合”层编写单元测试,并且难以为异步方法创建确定性测试,这些异步方法允许用户提前取消操作。

具体来说,在一些异步方法中,我们有代码对取消调用做出反应,并确保对象在完成之前处于适当状态。 我想确保这些代码路径被测试覆盖。

在这种情况下举例说明典型的异步方法的一些C#伪代码如下所示:

public void FooAsync(CancellationToken token, Action<FooCompletedEventArgs> callback) 
{
   if (token.IsCancellationRequested) DoSomeCleanup0();

   // Call the four helper methods, checking for cancellations in between each
   Exception encounteredException;
   try
   {
      MyDependency.DoExpensiveStuff1();
      if (token.IsCancellationRequested) DoSomeCleanup1();

      MyDependency.DoExpensiveStuff2();
      if (token.IsCancellationRequested) DoSomeCleanup2();

      MyDependency.DoExpensiveStuff3();
      if (token.IsCancellationRequested) DoSomeCleanup3();

      MyDependency.DoExpensiveStuff4();
      if (token.IsCancellationRequested) DoSomeCleanup4();

   }
   catch (Exception e)
   {
      encounteredException = e;
   }

   if (!token.IsCancellationRequested)
   {
      var args = new FooCompletedEventArgs(a bunch of params);
      callback(args);
   }
}

到目前为止,我所提出的解决方案涉及嘲笑由胶合层包装的基础MyDependency操作,并强制每个人在任意时间段内休眠。 然后我调用异步方法,并告诉我的单元测试在取消异步请求之前休眠数毫秒。

像这样的东西(以Rhino Mocks为例):

[TestMethod]
public void FooAsyncTest_CancelAfter2()
{
   // arrange
   var myDependency = MockRepository.GenerateStub<IMyDependency>();

   // Set these stubs up to take a little bit of time each so we can orcestrate the cancels
   myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100));
   myDependency.Stub(x => x.DoExpensiveStuff2()).WhenCalled(x => Thread.Sleep(100));
   myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => Thread.Sleep(100));
   myDependency.Stub(x => x.DoExpensiveStuff4()).WhenCalled(x => Thread.Sleep(100));

   // act
   var target = new FooClass(myDependency);

   CancellationTokenSource cts = new CancellationTokenSource();
   bool wasCancelled = false;

   target.FooAsync(
      cts.Token,
      args =>
      {
         wasCancelled = args.IsCancelled;
         // Some other code to manipulate FooCompletedEventArgs
      });

   // sleep long enough for two operations to complete, then cancel
   Thread.Sleep(250);
   cts.Cancel();

   // Some code to ensure the async call completes goes here

   //assert
   Assert.IsTrue(wasCancelled);
   // Other assertions to validate state of target go here
}

除了在单元测试中使用Thread.Sleep使我感到不安,更大的问题在于,如果偶尔会在构建服务器上进行测试,但是如果这种测试恰好处于重大负载下,则此类测试会失败。 异步呼叫太过分了,取消来得太迟。

任何人都可以提供更可靠的单元测试取消逻辑,用于这样的长时间运行操作吗? 任何想法,将不胜感激。


我会尝试使用mock以同步方式“模拟”异步行为。 而不是使用

myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100));

然后在任意数量的毫秒内设置取消标志,我将其设置为回调的一部分:

myDependency.Stub(x => x.DoExpensiveStuff1());
myDependency.Stub(x => x.DoExpensiveStuff2());
myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => cts.Cancel());
myDependency.Stub(x => x.DoExpensiveStuff4());

从代码的角度来看,这看起来好像在通话过程中发生了取消。


每个长时间运行的操作应该在事件开始运行时触发。

把这个事件挂在单元测试中。 这给出了具有确定性的结果,这些事件在将来可能有用。

链接地址: http://www.djcxy.com/p/8959.html

上一篇: Orchestrating cancellation in long

下一篇: Selenium syntax for Verifications that work with Hudson / jenkins