È una cattiva pratica applicare un ordine di esecuzione per i test unitari?

81

Sto scrivendo test per un progetto composto da più sottomoduli. Ogni test case che ho scritto è indipendente l'una dall'altra e ho cancellato tutti i dati tra i test.

Anche se i test sono eseguiti in modo indipendente, sto valutando l'applicazione di un ordine di esecuzione, in quanto alcuni casi richiedono più di un sottomodulo. Ad esempio, un sottomodulo sta generando dati e un altro sta eseguendo query sui dati. Se il sottomodulo che genera i dati contiene un errore, anche il test per il sottomodulo di query fallirà, anche se il sottomodulo stesso funziona correttamente.

Non riesco a lavorare con i dati fittizi, in quanto la funzionalità principale che sto testando è la connessione a un server remoto black box, che riceve solo i dati dal primo sottomodulo.

In questo caso, è OK applicare un ordine di esecuzione per i test o è una cattiva pratica? Mi sento come se ci fosse un odore in questa configurazione, ma non riesco a trovare un modo migliore.

modifica: la domanda è tratta da Come strutturare i test in cui un test è un'altra configurazione di test? come "precedente" test non è una configurazione, ma verifica il codice che esegue la configurazione.

    
posta Ali Rasim Kocal 03.04.2018 - 15:02
fonte

6 risposte

234

I can not work with dummy data, as the main functionality I am testing is the connection to a black box remote server, which only gets the data from the first submodule.

Questa è la parte fondamentale per me. Puoi parlare di "unit test" e di "correre indipendentemente l'uno dall'altro", ma sembrano tutti dipendenti da questo server remoto e dipendenti dal "primo sottomodulo". Quindi tutto sembra strettamente accoppiato e dipende dallo stato esterno. In quanto tale, in effetti stai scrivendo test di integrazione. Avere questi test eseguiti in un ordine specifico è abbastanza normale in quanto dipendono strongmente da fattori esterni. Un'esecuzione di test ordinata, con l'opzione di uscire anticipatamente dall'esecuzione del test se le cose vanno male è perfettamente accettabile per i test di integrazione.

Ma varrebbe anche la pena di dare una nuova occhiata alla struttura della tua app. Essere in grado di prendere in giro il primo sottomodulo e il server esterno consentirebbe quindi potenzialmente di scrivere veri test di unità per tutti gli altri sottomoduli.

    
risposta data 03.04.2018 - 15:57
fonte
32

Sì, è una cattiva pratica.

Generalmente, un test unitario ha lo scopo di testare una singola unità di codice (ad esempio una singola funzione basata su uno stato noto).

Quando si desidera testare una catena di eventi che potrebbero accadere in natura, si desidera uno stile di test diverso, ad esempio un test di integrazione. Questo è ancora più vero se si dipende da un servizio di terze parti.

Per testare le cose in questo modo, è necessario trovare un modo per iniettare i dati fittizi, ad esempio implementando un'interfaccia del servizio dati che rispecchia la richiesta web ma restituisce i dati noti da un file di dati fittizio locale.

    
risposta data 03.04.2018 - 15:07
fonte
16

L'ordine di esecuzione forzata che proponi ha senso solo se si interrompe anche l'esecuzione del test dopo il primo errore.

Interrompere l'esecuzione del test al primo errore significa che ogni esecuzione di test può rilevare solo un singolo problema e non può trovare nuovi problemi finché non sono stati risolti tutti i problemi precedenti. Se il primo test da eseguire rileva un problema che richiede un mese per risolverlo, allora durante quel mese in modo efficace non verranno eseguiti test.

Se non si interrompe l'esecuzione del test al primo errore, l'ordine di esecuzione forzata non ti compra nulla perché ogni test non superato deve comunque essere esaminato. Anche solo per confermare che il test sul sottomodulo della query non riesce a causa dell'errore identificato anche nel sottomodulo di generazione dei dati.

Il miglior consiglio che posso dare è scrivere i test in modo tale che sia facile identificare quando un errore in una dipendenza sta causando il fallimento del test.

    
risposta data 03.04.2018 - 15:32
fonte
7

L'odore a cui ti riferisci è l'applicazione dell'insieme sbagliato di vincoli e regole ai tuoi test.

I test unitari spesso vengono confusi con "test automatici" o "test automatizzati da parte di un programmatore".

I test delle unità devono essere piccoli, indipendenti e veloci.

Alcune persone erroneamente leggono questo come "i test automatici scritti da un programmatore devono essere piccoli indipendenti e veloci" . Ma significa semplicemente che se i tuoi test non sono piccoli, indipendenti e veloci, non sono Test unitari e quindi alcune delle regole per i Test di unità non dovrebbero, non possono, o non applicare per i tuoi test . Un esempio banale: dovresti eseguire i tuoi Test unitari dopo ogni build, cosa che non devi fare per i test automatici che non sono veloci.

Sebbene i tuoi test non siano Test unitari, puoi saltare una regola e avere una certa interdipendenza tra i test, ma hai anche scoperto che ci sono altre regole che potresti aver perso e che dovrai reintrodurre - qualcosa per lo scopo di un'altra domanda.

    
risposta data 04.04.2018 - 11:10
fonte
6

Come notato sopra, quello che stai facendo sembra essere un test di integrazione, tuttavia tu affermi che:

For example, a submodule is generating data, and another one is running queries on the data. If the submodule generating the data contains an error, the test for the query submodule will also fail, even if the submodule itself works fine.

E questo potrebbe essere un buon punto di partenza per il refactoring. Il modulo che esegue query sui dati non deve dipendere da un'implementazione concreta del primo modulo (generatrice di dati). Invece sarebbe meglio iniettare un'interfaccia contenente i metodi per arrivare a quei dati e questo può essere deriso per testare le query.

per es.

Se hai:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

Preferisci invece:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Ciò rimuove la dipendenza dalle query sull'origine dati e consente di impostare test unitari facilmente ripetibili per particolari scenari.

    
risposta data 04.04.2018 - 11:58
fonte
6

Le altre risposte indicano che i test di ordinamento sono errati (il che è vero il più delle volte), ma c'è una buona ragione per imporre l'ordine nell'esecuzione del test: assicurati che i tuoi test lenti (cioè test di integrazione) si svolgano dopo il tuo più veloce test (test che non si basano su altre risorse esterne). Ciò garantisce che tu esegua più test più velocemente, il che può accelerare il ciclo di feedback.

    
risposta data 04.04.2018 - 16:10
fonte

Leggi altre domande sui tag