2012-09-17 15 views
13

Leo otras publicaciones que ofrecen soluciones a esta pregunta. Sin embargo, sus soluciones requieren que se agregue código malicioso a mi aplicación para poder probarlo. Para mí, el código limpio es más importante que la prueba unitaria.Objetivo C: prueba unitaria dispatch_async block?

Uso dispatch_async en mi aplicación regularmente, y estoy teniendo problemas para probarlo. El problema es que el bloque se ejecuta después de que mi prueba ya está hecha, ya que se ejecuta de forma asíncrona en la cola principal. ¿Hay alguna manera de esperar de alguna manera hasta que se ejecute el bloque y luego continuar con la prueba?

NO deseo de pasar una terminación al bloque sólo a causa de la unidad de pruebas de

- (viod)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Test passes on this 
    [self.serviceClient fetchDataForUserId:self.userId]; 


    // Test fails on this because it's asynchronous 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.serviceClient fetchDataForUserId:self.userId]; 
    }); 
} 

- (void)testShouldFetchUserDataUsingCorrectId 
{ 
    static NSString *userId = @"sdfsdfsdfsdf"; 
    self.viewController.userId = userId; 
    self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]]; 

    [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId]; 
    [self.viewController view]; 
    [(OCMockObject *)self.viewController.serviceClient verify]; 
} 

Respuesta

37

Ejecutar el bucle brevemente principal para dejar que se llama el bloque asíncrono:

- (void)testShouldFetchUserDataUsingCorrectId { 
    static NSString *userId = @"sdfsdfsdfsdf"; 
    self.viewController.userId = userId; 
    self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]]; 

    [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId]; 
    [self.viewController view]; 
    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 
    [(OCMockObject *)self.viewController.serviceClient verify]; 
} 

supongo que esto podría fallar en un sistema con mucha carga, o si usted tiene un montón de otras cosas (temporizadores u otros bloques) yendo al hilo principal. Si ese es el caso, necesita ejecutar el bucle de ejecución más tiempo (lo que ralentiza su caso de prueba) o ejecutarlo repetidamente hasta que se cumplan las expectativas del objeto de prueba o se alcance un tiempo de espera (lo que requiere agregar un método al objeto de prueba consultar si se han cumplido sus expectativas).

+0

Usando este código, el bloque no recibe ninguna llamada, ni siquiera después de que se realiza la prueba. – aryaxt

+0

He revisado mi respuesta. Probé este enfoque más a fondo. –

+0

Gracias. Este método no parece ser confiable, pero no he encontrado una mejor solución, así que lo usaré con la esperanza de que no rompa mis pruebas. – aryaxt

10

Wrap la ejecución en un dispatch_group y luego esperar a que el grupo para terminar la ejecución de todos los bloques enviados a través dispatch_group_wait().

+0

Muy interesante. Nunca escuché hablar de dispatch_group antes. Es posible que no use NSOperationQueues nuevamente. El único problema que tuve con GCD fue la falta de esta funcionalidad – aryaxt

+0

@aryaxt Sí, los grupos de despacho son muy útiles, pero raramente se mencionan en ningún lado (ni siquiera los documentos oficiales de Apple son muy detallados) – JustSid

+0

Esta es una técnica muy útil. Debería recibir más ups. – e2l3n

0

Haga una envoltura dispatch_async con una firma de método similar que a su vez llame al dispatch_async real. Dependencia inyectar el envoltorio en su clase de producción y usar eso.

Luego crea una envoltura de simulacro, que graba bloques en cola y tiene un método adicional para ejecutar todos los bloques en cola sincrónicamente. Tal vez realice un "bloqueo" recursivo si los bloques que se están ejecutando a su vez pusieron en cola más bloques.

En sus pruebas unitarias, inyecte el envoltorio simulado en el sistema bajo prueba. Luego puede hacer que todo suceda sincrónicamente aunque el SUT crea que está haciendo un trabajo asincrónico.

dispatch_group suena como una buena solución también, pero requeriría que su clase de producción "supiera" hacer ping al grupo de despacho al final de los bloques que encola.

Cuestiones relacionadas