2009-09-08 7 views
5

Nei test di integrazione, i processi asincroni (metodi, servizi esterni) costituiscono un codice di prova molto difficile. Se invece, ho preso in considerazione la parte asincrona e creato una dipendenza e la sostituisco con una sincrona per il testing, sarebbe una "cosa buona"?In Test di integrazione, ha senso sostituire il processo Async con uno sincrono per motivi di test?

Sostituendo il processo asincrono con uno sincrono, non sto testando nello spirito del test di integrazione? Suppongo che sto assumendo che i test di integrazione si riferiscono a test vicino alla cosa reale.

risposta

2

Abbiamo un numero di test di unità automatizzate che inviano richieste asincrone e che devono testare l'output/i risultati. Il modo in cui lo gestiamo è di eseguire effettivamente tutti i test come se fossero parte dell'applicazione reale, in altre parole le richieste asincrone rimangono asincrone. Ma il test harness agisce in modo sincrono: invia la richiesta asincrona, dorme per [fino a] un periodo di tempo (il massimo in cui ci si aspetterebbe che un risultato sia prodotto), e se ancora nessun risultato è disponibile, allora il test è fallito. Esistono callback, quindi in quasi tutti i casi il test viene risvegliato e continua a funzionare prima che il timeout sia scaduto, ma i timeout indicano che un errore (o un cambiamento nelle prestazioni previste) non bloccherà/interromperà l'intera suite di test.

Questo ha alcuni vantaggi:

  • Il test unità è molto vicino agli schemi di chiamata effettivi dell'applicazione
  • Nessun nuovo codice/stub sono necessari per rendere il codice dell'applicazione (il codice in fase di sperimentazione) eseguire in modo sincrono
  • performance è testato in modo implicito: Se il test ha dormito per un periodo troppo breve, poi qualche caratteristica di prestazione è cambiato, e che ha bisogno di guardare in

L'ultimo punto potrebbe richiedere una piccola quantità di spiegazione. I test delle prestazioni sono importanti e vengono spesso esclusi dai piani di test. Il modo in cui vengono eseguiti questi test unitari, richiedono molto più tempo (tempo di esecuzione) che se avessimo riorganizzato il codice per fare tutto in modo sincrono. Tuttavia, in questo modo, le prestazioni vengono testate implicitamente e i test sono più fedeli al loro utilizzo nell'applicazione. Inoltre, tutte le nostre infrastrutture di accodamento dei messaggi vengono testate "gratuitamente" lungo il percorso.

Edit: Aggiunta una nota sul callback

0

Cosa stai testando? Il comportamento della tua classe in risposta a determinati stimoli? In tal caso, le prese in giro non sono adeguate?

Class Orchestrator implements AsynchCallback { 

    TheAsycnhService myDelegate; // initialised by injection 

    public void doSomething(Request aRequest){ 
      myDelegate.doTheWork(aRequest, this) 
    } 

    public void tellMeTheResult(Response aResponse) { 
      // process response 
    } 
} 

Il test può fare qualcosa di simile

Orchestrator orch = new Orchestrator(mockAsynchService); 

orch.doSomething(request); 

// assertions here that the mockAsychService received the expected request 

// now either the mock really does call back 
// or (probably more easily) make explicit call to the tellMeTheResult() method 

// assertions here that the Orchestrator did the right thing with the response 

Nota che non c'è alcuna vera trasformazione asynch qui, e il finto sé ha bisogno di avere alcuna logica se non per consentire la verifica della ricevuta della richiesta corretta. Per un test unitario dell'Orchestratore questo è sufficiente.

Ho usato la variazione this sull'idea durante il test dei processi BPEL in WebSphere Process Server.

7

Bella domanda.

In un test di unità questo approccio avrebbe senso, ma per i test di integrazione si dovrebbe testare il sistema reale come si comporterà nella vita reale.Ciò include qualsiasi operazione asincrona e qualsiasi effetto collaterale che possono avere - questo è il posto più probabile per l'esistenza di bachi ed è probabilmente il punto in cui dovresti concentrare il test non lo comporti fuori.

Spesso utilizzo un approccio "waitFor" in cui eseguo il polling per verificare se è stata ricevuta una risposta e dopo un po 'di tempo se non lo è. Una buona implementazione di questo modello, anche se si può ottenere il succo specifico di Java, è lo JUnitConditionRunner. Ad esempio:

conditionRunner = new JUnitConditionRunner(browser, WAIT_FOR_INTERVAL, WAIT_FOR_TIMEOUT); 

protected void waitForText(String text) { 
    try { 
     conditionRunner.waitFor(new Text(text)); 
    } catch(Throwable t) { 
     throw new AssertionFailedError("Expecting text " + text + " failed to become true. Complete text [" + browser.getBodyText() + "]"); 
    } 
} 
+0

+1 per concentrarsi sui test di integrazione. Sono d'accordo che questo può essere dove si nascondono i cattivi difetti. – djna

+0

Al momento utilizzo l'approccio "waitFor". Tuttavia, non tutte le elaborazioni asincrone sono uguali. Nella mia app, l'elaborazione asincrona viene gestita da Windows message pump (WindowsFormsSynchronizationContext in .NET) e per qualche motivo, non mi piace come viene utilizzato nei test. Quindi, la mia domanda è di funzionare se possibile. Ma hai ragione - dovremmo testare il più possibile vicino alla cosa reale. –

+0

Inoltre, la mia app è un'app rich client, richiede/consente di mostrare l'interfaccia utente durante i test di integrazione? Il meccanismo asincrono è strettamente legato all'interfaccia utente (beh, è ​​una pompa di messaggi di Windows e non funziona senza l'interfaccia utente). Posso modificare il codice di test in modo che l'interfaccia utente sia presente ma non interferisca, ma non sono sicuro di come di fronte all'integrazione continua. Sono tentato di sostituire il messaggio pump basato su async con qualcos'altro per motivi di testing, ma sembra che ci sia un sacco di lavoro. –