单元测试异步事件

时间:2016-06-16 16:22:12

标签: c# multithreading unit-testing asynchronous

我有一个设置,更改类的变量会引发一个异步运行的事件,我试图进行单元测试。

class A {

    Action<int> OnIntChange;

    private int _a;
    public int a {
        set {
            OnIntChange(value);
        }
    }
}

class B {

    RaiseAsyncEvent(int value) {
        Task.Factory.StartNew(() => {c = value;});
    }

    int c;
}

以前一切都是同步的,所以我可以很容易地进行单元测试:

B b = new B();
A.OnIntChange += b.RaiseAsyncEvent;    
A.a = 10;
Assert.AreEqual(10, A.b.c);

然而现在失败了。我无法使用ManualResetEvents,因为我没有直接提出此事件,并且无法在没有重复代码的情况下获胜。看来我唯一的选择是在调用A.a = 10之后添加一个System.Threading.Thread.Sleep()调用,但我正在寻找其他选项。建议?

~~~~~~~~~~编辑~~~~~~~~

我正在研究的一个可能的解决方案是让RaiseAsyncEvent返回它创建的任务并为该任务设置一个字段。类似的东西:

class B {

    Task task;

    RaiseAsyncEvent(int value) {
        task = Task.Factory.StartNew(() => {c = value;});
    }

    int c;
}


B b = new B();
A.OnIntChange += b.RaiseAsyncEvent; 
b.task.Wait();
A.a = 10;
Assert.AreEqual(10, A.b.c);

~~~~~~~~~~进一步编辑~~~~~~~~~~

我最终要做的就是将B改为以下内容:

class B {

    bool RunAsync = true;

    RaiseAsyncEvent(int value) {
        Task task = Task.Factory.StartNew(() => {c = value;});
        if(!RunAsync) { task.Wait();}
    }

    int c;
}

并将测试更改为:

B b = new B();
b.RunAsync = false;
A.OnIntChange += b.RaiseAsyncEvent;    
A.a = 10;
Assert.AreEqual(10, A.b.c);

1 个答案:

答案 0 :(得分:2)

您可以在B的构造函数中传递TaskSchedulerTaskScheduler的自定义版本可以等待单元测试中所有任务的完成。

class B {
   public B(TaskScheduler taskScheduler)
   {
      _taskScheduler = taskScheduler;
   }   


   public B(): this(TaskScheduler.Default)
   {
   }


   public void RaiseAsyncEvent(int value)
   {
       Task.Factory.StartNew(() => {c = value;}, CancellationToken.None,
         TaskCreationOptions.DenyChildAttach, _taskScheduler);
   }

   TaskScheduler _taskScheduler;

}

ConcurrentExclusiveSchedulerPair有能力等待我们寻求的完成。

// Unit test

 var schedulerPair = new ConcurrentExclusiveSchedulerPair();

 B b = new B(schedulerPair.ConcurrentScheduler);
 A.OnIntChange += b.RaiseAsyncEvent;    
 A.a = 10;

 schedulerPair.Complete();
 schedulerPair.Completion.Wait();

 Assert.AreEqual(10, A.b.c);

当您需要关注多个异步操作时,它会扩展到更高级的场景:

 B b1 = new B(schedulerPair.ConcurrentScheduler);
 B b2 = new B(schedulerPair.ConcurrentScheduler);
 B b3 = new B(schedulerPair.ConcurrentScheduler);

 A.OnIntChange += b1.RaiseAsyncEvent;    
 A.OnIntChange += b2.RaiseAsyncEvent;    
 A.OnIntChange += b3.RaiseAsyncEvent;    
 A.a = 10;

 schedulerPair.Complete();
 schedulerPair.Completion.Wait();

 Assert.AreEqual(10, b1.c);
 Assert.AreEqual(10, b2.c);
 Assert.AreEqual(10, b3.c);

编辑:TaskScheduler实例也可以使用静态属性共享,在这种情况下B构造函数签名不会更改。

static class EventScheduler
{

  public static TaskScheduler TaskScheduler
  {
      get {return _taskScheduler; }
      set {_taskScheduler = value; }
  }   

  static TaskScheduler _taskScheduler = TaskScheduler.Default;

  public static Task RunAsync(Action<T> action)
  {
      return Task.Factory.StartNew(() => {c = value;}, 
       CancellationToken.None, TaskCreationOptions.DenyChildAttach,
      _taskScheduler);
   }
}

class B 
{
   public void RaiseAsyncEvent(int value)
   {
       EventScheduler.RunAsync(()=>{c = value;});
   }
 }

 // Unit test

 var schedulerPair = new ConcurrentExclusiveSchedulerPair();
 EventScheduler.TaskScheduler = schedulerPair.ConcurrentScheduler;

 B b = new B();
 A.OnIntChange += b.RaiseAsyncEvent;    
 A.a = 10;

 schedulerPair.Complete();
 schedulerPair.Completion.Wait();

 Assert.AreEqual(10, A.b.c);