我有一个设置,更改类的变量会引发一个异步运行的事件,我试图进行单元测试。
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);
答案 0 :(得分:2)
您可以在B的构造函数中传递TaskScheduler。TaskScheduler
的自定义版本可以等待单元测试中所有任务的完成。
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);