我想对我执行的方法和异步操作进行单元测试:
Task.Factory.StartNew(() =>
{
// method to test and return value
var result = LongRunningOperation();
});
我在单元测试中编写必要的方法等(用c#编写),但问题是在断言测试之前异步操作没有完成。
我该如何解决这个问题?我应该创建TaskFactory的模拟或任何其他单元测试异步操作的技巧吗?
答案 0 :(得分:7)
你必须有一些伪造任务创造的方法。
如果您将Task.Factory.StartNew
调用移动到某个依赖项(ILongRunningOperationStarter
),那么您可以创建一个替代实现,该实现使用TaskCompletionSource
来创建完全完成您所需位置的任务。< / p>
它可能有点毛茸茸,但可以完成。我blogged about this前一段时间 - 单元测试接收任务的方法,这当然使事情变得更容易。它是在C#5中的异步/等待的上下文中,但适用相同的原则。
如果你不想伪造整个任务创建,你可以更换任务工厂,并控制时间 - 但我怀疑这将更加毛茸茸,说实话。
答案 1 :(得分:4)
我建议在你的方法中使用特殊的单元测试实现存根TaskScheduler。您需要准备代码以使用注入的TaskScheduler:
private TaskScheduler taskScheduler;
public void OperationAsync()
{
Task.Factory.StartNew(
LongRunningOperation,
new CancellationToken(),
TaskCreationOptions.None,
taskScheduler);
}
在单元测试中,您可以使用this blog post中描述的DeterministicTaskScheduler在当前线程上运行新任务。在您点击第一个断言语句之前,您的“异步”操作将完成:
[Test]
public void ShouldExecuteLongRunningOperation()
{
// Arrange: Inject task scheduler into class under test.
DeterministicTaskScheduler taskScheduler = new DeterministicTaskScheduler();
MyClass mc = new MyClass(taskScheduler);
// Act: Let async operation create new task
mc.OperationAsync();
// Act: Execute task on the current thread.
taskScheduler.RunTasksUntilIdle();
// Assert
...
}
答案 2 :(得分:0)
尝试这样的事情......
object result = null;
Task t = Task.Factory.StartNew(() => result = LongRunningThing());
Task.Factory.ContinueWhenAll(new Task[] { t }, () =>
{
Debug.Assert(result != null);
});
答案 3 :(得分:0)
设置UI和后台任务计划,并在单元测试中用这个替换它们。
下面的代码是从互联网上复制的,遗憾的是缺少对作者的引用:
public class CurrentThreadTaskScheduler : TaskScheduler
{
protected override void QueueTask(Task task)
{
TryExecuteTask(task);
}
protected override bool TryExecuteTaskInline(
Task task,
bool taskWasPreviouslyQueued)
{
return TryExecuteTask(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return Enumerable.Empty<Task>();
}
public override int MaximumConcurrencyLevel => 1;
}
所以要测试代码:
public TaskScheduler TaskScheduler
{
get { return taskScheduler ?? (taskScheduler = TaskScheduler.Current); }
set { taskScheduler = value; }
}
public TaskScheduler TaskSchedulerUI
{
get { return taskSchedulerUI ?? (taskSchedulerUI = TaskScheduler.FromCurrentSynchronizationContext()); }
set { taskSchedulerUI = value; }
}
public Task Update()
{
IsBusy = true;
return Task.Factory.StartNew( () =>
{
LongRunningTask( );
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler )
.ContinueWith( t => IsBusy = false, TaskSchedulerUI );
}
您将编写以下单元测试:
[Test]
public void WhenUpdateThenAttributeManagerUpdateShouldBeCalled()
{
taskScheduler = new CurrentThreadTaskScheduler();
viewModel.TaskScheduler = taskScheduler;
viewModel.TaskSchedulerUI = taskScheduler;
viewModel.Update();
dataManagerMock.Verify( s => s.UpdateData( It.IsAny<DataItem>>() ) );
}