модульное тестирование асинхронной операции

Я хочу провести модульное тестирование метода, который у меня есть, который выполняет и асинхронную операцию:

<code> Task.Factory.StartNew(() =>
        {
            // method to test and return value
            var result = LongRunningOperation();
        });
</code>

В моем модульном тесте я пишу необходимые методы и т. Д. (Написанные на c #), но проблема в том, что асинхронная операция не завершена, прежде чем я выполню тест.

Как я могу обойти это? Должен ли я создать макет TaskFactory или любые другие советы для модульного тестирования асинхронной операции?

Ответы на вопрос(4)

object result = null;
Task t =  Task.Factory.StartNew(() => result = LongRunningThing()); 


Task.Factory.ContinueWhenAll(new Task[] { t }, () => 
{
   Debug.Assert(result != null);
});

У вас должен быть какой-то способ подделать создание задачи.

Если вы переместилиTask.Factory.StartNew вызов к некоторой зависимости (ILongRunningOperationStarter) тогда вы могли бы создать альтернативную реализацию, которая использовалаTaskCompletionSource создавать задачи, которые выполняются именно там, где вы хотите их.

Это может стать немного волосатым, но этоcan быть сделано янаписал об этом в блоге некоторое время назад - модульное тестирование метода, которыйreceived задачи для начала, которые, конечно, сделали вещи проще. Это в контексте async / await в C # 5, но применяются те же принципы.

Если вы не хотите подделывать всю задачу создания задачи, вы можете заменить фабрику задач и таким образом контролировать время выполнения, но, если честно, я подозреваю, что это было бы даже опаснее.

еализацией для модульных тестов. Вам необходимо подготовить свой код для использования внедренного TaskScheduler:

 private TaskScheduler taskScheduler;

 public void OperationAsync()
 {
     Task.Factory.StartNew(
         LongRunningOperation,
         new CancellationToken(),
         TaskCreationOptions.None, 
         taskScheduler);
 }

В своем модульном тесте вы можете использовать DeterministicTaskScheduler, описанный вэтот блог запустить новое задание в текущем потоке. Ваш "асинхронный" операция будет завершена до того, как вы нажмете свое первое утверждение assert:

[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
    ...
}

Установите пользовательский интерфейс и графики фоновых задач и замените их в модульном тесте на этот.

Ниже код был скопирован из интернета, извините за отсутствующую ссылку на автора:

  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>>() ) );
}

ВАШ ОТВЕТ НА ВОПРОС