2012-05-11 7 views
13

Quiero unidad de prueba un método que tengo que realiza y el funcionamiento asíncrono:unidad de operación de prueba asíncrono

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

I stub los métodos necesarios etc en mi unidad de prueba (escrita en C#), pero el problema es que la operación de sincronización no ha terminado antes de afirmar la prueba.

¿Cómo puedo evitar esto? ¿Debo crear un simulacro de TaskFactory u otros consejos para probar la unidad en una operación asíncrona?

Respuesta

6

Tendría que haber alguna manera de fingir la creación de tareas.

Si movió la llamada Task.Factory.StartNew a alguna dependencia (ILongRunningOperationStarter), entonces podría crear una implementación alternativa que utilizara TaskCompletionSource para crear tareas que completen exactamente donde las desea.

Puede ponerse un poco peludo, pero puede hacerse. I blogged about this hace un tiempo - unidad de prueba de un método que recibió tareas para empezar, que por supuesto hizo las cosas más fáciles. Está en el contexto de async/await en C# 5, pero se aplican los mismos principios.

Si no quiere falsificar toda la creación de tareas, podría reemplazar la fábrica de tareas y controlar el tiempo de esa manera, pero sospecho que sería aún más peludo, para ser sincero.

+0

https://codeblog.jonskeet.uk/2011/11/25/eduasync-part-17-unit-testing/ – luviktor

+0

@luviktor: corregido, gracias. (La próxima vez, siempre puede sugerir la edición usted mismo). –

0

intentar algo como esto ...

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


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

yo propondría a un código auxiliar TaskScheduler en su método con una aplicación especial para las pruebas unitarias. Es necesario preparar el código para utilizar un TaskScheduler inyectada:

private TaskScheduler taskScheduler; 

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

en su unidad de prueba se puede utilizar el DeterministicTaskScheduler describe en this blog post para ejecutar la nueva tarea en el flujo actual. Su operación 'asíncrono' estará terminado antes de llegar a su primera sentencia 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 
    ... 
} 
0

Set de interfaz de usuario y la tarea de fondo schedulars y reemplazarlos en la prueba de la unidad con éste.

A continuación código fue copiado de Internet, lo siento por falta de referencia al autor:

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

Así que para probar el código:

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

Usted escribirá siguiente unidad de prueba:

[Test] 
public void WhenUpdateThenAttributeManagerUpdateShouldBeCalled() 
{ 
    taskScheduler = new CurrentThreadTaskScheduler(); 
    viewModel.TaskScheduler = taskScheduler; 
    viewModel.TaskSchedulerUI = taskScheduler; 
    viewModel.Update(); 
    dataManagerMock.Verify(s => s.UpdateData(It.IsAny<DataItem>>())); 
}