2011-08-22 10 views
11

La domanda più completa è, data una dipendenza che prevede una richiamata come parametro, come posso scrivere un test unitario che copra la logica di callback e ancora in grado di simulare la dipendenza?Come testare la logica di callback del test?

public class DoStuff { 
    public void runThis(Runnable callback) { 
     // call callback 
    } 
} 

public class ClassUnderTest { 
    private DoStuff stuffToDo; 

    public void methodUnderTest() { 
     this.stuffToDo.runThis(/*a runnable with some logic*/) 
    } 
} 

Nell'esempio precedente, avrei deridere stuffToDo poiché devo verificare chiamate e uscite finte di chiamate di metodo. Tuttavia, il mocking runThis ha come risultato che la logica di callback non è stata testata. Inoltre, la logica di callback sembra essere privata, quindi non mi aspetto di testarla direttamente; forse è un equivoco da parte mia.

Poiché i callback sono utilizzati piuttosto estensivamente, mi aspetto che ci sia un metodo comune per testarli ma non l'ho trovato.

+0

Il callback deve essere testato solo in questo contesto? In tal caso, perché non scrivere semplicemente test per i vari callback che pianifichi di trasmettere, che esprimono la tua aspettativa dei loro risultati? Mi sto perdendo qualcosa? – Jonah

+0

Hai provato a usare CountDownLatch come in questo esempio: http://stackoverflow.com/a/3802487/2301224 – Baker

risposta

10

Non è possibile in una singola prova. Se si prende in giro qualcosa, il suo deriso, il che significa che può solo verificare gli argomenti e simulare i valori di ritorno (che si configurano nel test).

In effetti, l'intero punto di prendere in giro qualcosa è quello di testare ciò che utilizza la simulazione in isolamento. Se si desidera un test unitario di DoStuff, non si deve preoccuparsi di utilizzare alcune implementazioni di callback che potrebbero funzionare o meno. Si prende in giro la richiamata in modo da non doversi preoccupare di ciò.

Si può ancora avere buone prove da testare il codice di callback in isolamento, e testare l'utente di richiamata in isolamento (usando un mock per la richiamata) e forse lanciando un test di integrazione per buona misura, in cui si usa il componente completamente configurato nel suo complesso.

1

Qui fondamentalmente si desidera testare la classe DoStuff. Ciò significa che è necessario testare tutti i metodi all'interno di DoStuff, giusto? Quindi, in questo caso, è necessario, invece di prendere in giro il materiale stuffToDo, iniettare un Runnable deriso in stuffToDo. E poi controlla se il tuo runnable è stato eseguito con successo o meno.

Ma se si hanno altre funzionalità nella classe, è possibile avere un test separato e deriderle.

+1

Voglio testare ClassUnderTest che accetta DoStuff come dipendenza. –

0

Nel tuo caso particolare, sembra che tu abbia già provato DoStuff (o non ti interessi più da quando è preso in giro) e ora stai specificamente testando unitamente uno specifico Runnable che hai progettato. In questo caso, callback suona esattamente come si desidera testare, nello stesso modo in cui qualcuno potrebbe voler testare direttamente una strategia di database o una strategia in memoria direttamente.

Se questo è quello che stai tentando, puoi testarlo in una scatola nera, esercitandolo attraverso ClassUnderTest nel miglior modo possibile. O puoi creare un'imbracatura di test sul tuo Runnable particolare. Se si sta rilasciando questo codice e non si desidera rendere accessibile il proprio cablaggio di test, è possibile rendere privati ​​i metodi di test harness e condividere i dettagli in modo specifico con l'assembly di test dell'unità.

Look here per informazioni su come creare gruppi di amici. In genere firmo il mio codice di test unitario in modo da non dover utilizzare un compilatore di riga di comando. Suppongo che dipenda dal tuo ambiente di costruzione.

+0

No, ClassUnderTest è la classe da testare. Sto prendendo in giro DoStuff. –

+0

Ho modificato il nome della classe per riflettere questo. Non influisce sulla risposta. –

0

Se si utilizza EasyMock, è possibile utilizzare andStubAnswer per chiamare il runnable.

doSomethingMock.runThis(runnable); 
expectLastCall().andStubAnswer(new IAnserable<Void>() { 
    Runnable runnable = (Runnable)getCurrentArguments()[0]; 
    runnable.run(); 
    return null; 
}); 

Immagino che altre strutture di derisione contengano qualcosa di simile.

0

Che ne dici di un FaStuff contraffatto che invoca incondizionatamente il Runnable?

Quindi si percepisce solo per gli effetti - modifiche da osservare se è stata eseguita la richiamata.

Problemi correlati