在长时间运行单元测试策划取消 [英] Orchestrating cancellation in long-running unit test

查看:134
本文介绍了在长时间运行单元测试策划取消的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写单元测试我的应用程序的胶水层,和我有困难创造异步方法,使用户能够pmaturely取消操作$ P $确定测试。

I am writing unit tests for the "glue" layer of my application, and am having difficulty creating deterministic tests for asynchronous methods that allow the user to cancel the operation prematurely.

具体而言,在一些异步方法,我们有code作出反应的号召取消,并确保对象是处于正常状态之前完成。我想确保这些code路径测试所覆盖。

Specifically, in a few async methods we have code that reacts to the cancellation of the call and ensures that the object is in the proper state prior to completing. I would like to ensure that these code paths are covered by tests.

一些C#伪code在此方案中举例说明一个典型的异步方法如下:

Some C# pseudo code exemplifying a typical async method in this scenario is as follows:

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 操作,并迫使每个解决方案睡的任意时间时间。然后,我调用异步方法,并告诉我的单元测试,以取消异步请求之前的毫秒数睡觉。

The solution that I have come up with so far involves mocking the underlying MyDependency operations that are wrapped by the glue layer, and forcing each to sleep for an arbitrary period of time. I then invoke the async method, and tell my unit test to sleep for a number of milliseconds before canceling the async request.

这样的事情(使用犀牛制品为例):

Something like this (using Rhino Mocks as an example):

[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让我反胃事实上,更大的问题是,有时这样的测试,我们构建服务器上失败,如果它正好是显著负载下。异步调用得太远了,取消来得太迟。

Aside from the fact that using Thread.Sleep in a unit test makes me queasy, the bigger problem is that sometimes tests like this fail on our build server if it happens to be under significant load. The async call gets too far, and the cancel comes too late.

任何人都可以像这样长时间运行的操作提供单元测试取消逻辑更可靠的方式?任何想法将AP preciated。

Can anyone provide a more reliable manner of unit testing cancellation logic for long running operations like this? Any ideas would be appreciated.

推荐答案

我会尝试用嘲笑以同步的方式来模拟异步行为。而不是使用

I would try to use mocks to "simulate" asynchronous behaviour in a synchronous way. Instead of using

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

,然后设置毫秒的任何数字内取消标志,我只想把它作为回调的一部分:

and then setting the cancellation flag within whatever number of miliseconds, I would just set it as part of the callback:

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

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

from your code's perspective this will look as if the cancellation happened during the call.

这篇关于在长时间运行单元测试策划取消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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