Come facciamo a velocizzare i test unitari?

39

Abbiamo raggiunto il punto nel nostro progetto in cui abbiamo quasi un migliaio di test e la gente ha smesso di preoccuparsi di eseguirli prima di fare un check-in perché ci vuole così tanto tempo. Al massimo eseguono i test rilevanti per il codice che hanno modificato e, nel peggiore dei casi, lo controllano semplicemente senza test.

Credo che questo problema sia dovuto al fatto che la soluzione è cresciuta fino a 120 progetti (di solito facciamo progetti molto più piccoli e questa è solo la seconda volta che eseguiamo TDD correttamente) e il tempo di generazione + test è cresciuto di circa due -tre minuti sui macchinari minori.

Come riduciamo il tempo di esecuzione dei test? Ci sono tecniche? Fa finta di più? Fa finta di meno? Forse i test di integrazione più grandi non dovrebbero essere eseguiti automaticamente quando si eseguono tutti i test?

Modifica: come risposta a molte delle risposte, utilizziamo già elementi di configurazione e un server di build, in questo modo so che i test hanno esito negativo. Il problema (in realtà un sintomo) è che continuiamo a ricevere messaggi su build falliti. Eseguire test parziali è qualcosa che la maggior parte delle persone fa, ma non tutti. e per quanto riguarda i test, sono in realtà piuttosto ben fatti, usano falsi per tutto e non c'è affatto IO.

    
posta Ziv 25.01.2013 - 17:45
fonte

13 risposte

51

Una possibile soluzione sarebbe spostare la porzione di test dalle macchine di sviluppo ad una configurazione di integrazione continua (ad esempio Jenkins ) usando il controllo della versione software di qualche gusto ( git , svn , ecc ...).

Quando il nuovo codice deve essere scritto, lo sviluppatore dato creerà un ramo per qualunque cosa stiano facendo nel repository. Tutto il lavoro verrà svolto in questo ramo e potranno commettere le modifiche al ramo in qualsiasi momento senza compromettere la riga principale del codice.

Quando la funzione, la correzione di bug o qualsiasi altra cosa su cui stanno lavorando è stata completata, quel ramo può essere riunito nel trunk (o comunque si preferisce farlo) dove vengono eseguiti tutti i test unitari. Se un test fallisce, l'unione viene rifiutata e lo sviluppatore viene informato in modo che possa correggere gli errori.

Puoi anche fare in modo che il tuo server CI esegua i test unitari su ciascun ramo di funzionalità mentre vengono eseguiti i commit. In questo modo lo sviluppatore può apportare alcune modifiche, eseguire il commit del codice e lasciare che il server esegua i test in background mentre continuano a lavorare su altre modifiche o altri progetti.

Un'ottima guida per un modo di fare una tale configurazione può essere trovata qui (git specifico ma dovrebbe funzionare per altri sistemi di controllo della versione): link

    
risposta data 25.01.2013 - 18:18
fonte
32

La maggior parte dei test delle unità dovrebbe richiedere meno di 10 millisecondi. Avere "quasi mille test" è nulla e dovrebbe impiegare forse per alcuni secondi.

Se non lo sono, allora dovresti smettere di scrivere test di integrazione altamente integrati (a meno che non sia quello di cui il codice ha bisogno) e iniziare a scrivere buoni test unitari (partendo da un codice ben disaccoppiato e un uso corretto dei falsi / deride / stub / etc). Questo accoppiamento influirà sulla qualità del test e sul tempo necessario per scriverlo, quindi non si tratta solo di ridurre il tempo di esecuzione del test.

    
risposta data 25.01.2013 - 18:07
fonte
16

Ci sono diversi approcci che ho usato per risolvere un problema simile:

  1. Verifica il tempo di esecuzione e trova tutti i test più lenti, quindi analizza il motivo per cui impiegano così tanto tempo a eseguire .
  2. Hai 100 progetti, forse non hai bisogno di costruirli e testarli ogni volta? Potresti eseguire tutti gli incontri solo a build di notte? Crea diverse configurazioni di build 'veloci' per l'uso quotidiano . Il eseguirà un limitato numero di progetti non connessi relativi a parti "calde" del tuo processo di sviluppo corrente.
  3. Schiva e isola tutto ciò che potresti , evita il disco / I / O di rete ogni volta che è possibile
  4. Quando non è possibile isolare tali operazioni, potrebbe esserci un test di integrazione? Potrebbe essere possibile pianificare test di integrazione per le build notturne ?
  5. Controlla tutti i singleton occasionali, che mantengono riferimenti a istanze / risorse e che consumano memoria, questo potrebbe portare a un peggioramento delle prestazioni durante l'esecuzione di tutti i test.

Inoltre, puoi utilizzare i seguenti strumenti per semplificarti la vita e velocizzare i test

  1. Gated commit alcuni server CI possono essere configurati per eseguire build e test prima di eseguire il commit del codice sul repository di origine. Se qualcuno esegue il commit del codice senza eseguire tutti i test in anticipo, che contiene anche test non riusciti, verrà rifiutato e restituito all'autore.
  2. Configura il server CI per eseguire test in parallelo : utilizzando più macchine o processi. Esempi sono pnunit e configurazione CI con diversi nodi.
  3. Plug-in di test continuo per gli sviluppatori, che eseguirà automaticamente tutti i test durante la scrittura del codice.
risposta data 25.01.2013 - 18:35
fonte
10

0. Ascolta i tuoi programmatori.

Se non stanno eseguendo i test, significa che percepiscono il costo (in attesa dell'esecuzione dei test, che si occupa dei falsi errori) per essere maggiore del valore (catturare i bug subito). Riduci i costi, aumenta il valore e le persone eseguiranno i test continuamente.

1. Rendi i tuoi test affidabili al 100%.

Se mai hai dei test che falliscono con i falsi negativi, gestiscili subito. Correggili, cambiali, eliminali, tutto ciò che serve per garantire il 100% di affidabilità. (Va bene avere una serie di test non affidabili, ma comunque utili che è possibile eseguire separatamente, ma il corpo principale dei test deve essere affidabile.)

2. Cambia i tuoi sistemi per garantire che tutti i test passino sempre.

Utilizza i sistemi di integrazione continua per assicurare che solo i commit passanti vengano uniti al ramo principale / ufficiale / release / qualunque.

3. Cambia la tua cultura per valutare il 100% dei test di passaggio.

Insegna la lezione che un compito non viene "completato" fino a quando il 100% dei test non passa e si è unito al ramo principale / ufficiale / release / qualunque.

4. Fai i test velocemente.

Ho lavorato su progetti in cui i test richiedono un secondo e su progetti in cui vengono eseguiti tutto il giorno. Esiste una strong correlazione tra il tempo necessario per eseguire i test e la mia produttività.

I test più lunghi richiedono esecuzione, meno spesso li eseguirai. Ciò significa che dovrai andare più a lungo senza ricevere feedback sulle modifiche che stai apportando. Significa anche che andrai più a lungo tra i commit. Commettere più spesso significa passi più piccoli che sono più facili da unire; commettere la cronologia è più facile da seguire; trovare un bug nella storia è più semplice; anche il rollback è più facile.

Immagina test eseguiti così velocemente che non ti dispiaccia automaticamente eseguirli ogni volta che compili.

Fare test veloci può essere difficile (questo è quello che l'OP chiedeva, giusto!). Disaccoppiamento è la chiave. I finti / falsi sono OK, ma penso che si possa fare meglio rifattorizzando per rendere inutili i finti / falsi. Vedi il blog di Arlo Belshee, iniziando con link .

5. Rendi i test utili.

Se i test non falliscono quando si sbaglia, allora qual è il punto? Insegnati a scrivere test che catturino gli errori che potresti creare. Questa è un'abilità a sé stante e richiederà molta attenzione.

    
risposta data 26.01.2013 - 20:53
fonte
4

Un paio di minuti è OK per i test unitari. Tuttavia, tieni presente che esistono 3 tipi principali di test:

  1. Test di unità - testa ogni "unità" (classe o metodo) indipendentemente dal resto del progetto
  2. Test di integrazione: verifica il progetto nel suo complesso, solitamente effettuando chiamate nel programma. Alcuni progetti che ho visto combinano questo con i test di regressione. Qui c'è molto meno derisione dei test unitari
  3. Prove di regressione: verifica il progetto completo nel suo insieme, poiché la suite di test è un utente finale. Se si dispone di un'applicazione console, si utilizzerà la console per eseguire e testare il programma. Non esponi mai questi test a internals e qualsiasi utente finale del tuo programma dovrebbe (in teoria) essere in grado di eseguire la tua suite di test di regressione (anche se non lo faranno mai)

Questi sono elencati in ordine di velocità. I test unitari dovrebbero essere veloci. Non cattureranno tutti i bug, ma stabiliscono che il programma è decentemente equilibrato. I test unitari dovrebbero essere eseguiti in 3 minuti o meno o hardware decente. Dici che hai solo 1000 test unitari e ci vogliono 2-3 minuti? Bene, probabilmente è OK.

Cose da verificare:

  • Assicurati che i test delle tue unità e quelli di integrazione siano separati. I test di integrazione saranno sempre più lenti.

  • Verifica che i test dell'unità siano eseguiti in parallelo. Non c'è motivo per non farlo se sono veri test unitari

  • Verifica che i tuoi test unitari siano "senza dipendenza". Non dovrebbero mai accedere a un database o al filesystem

Oltre a questo, i tuoi test non sembrano troppo male in questo momento. Tuttavia, per riferimento, uno dei membri di un mio amico in un team Microsoft ha 4.000 test unitari che vengono eseguiti in meno di 2 minuti su hardware decente (ed è un progetto complicato). È possibile avere test unitari rapidi. Eliminare le dipendenze (e prendere in giro solo quanto necessario) è la cosa principale per ottenere velocità.

    
risposta data 25.01.2013 - 21:55
fonte
3

Allena i tuoi sviluppatori su Personal Software Process (PSP) aiutandoli a comprendere e migliorare le loro prestazioni usando più disciplina. Scrivere codice non ha nulla a che fare con lo sbattere le dita su una tastiera e poi premere un pulsante di compilazione e check-in.

PSP era molto popolare in passato quando la compilazione del codice era un processo che richiedeva molto tempo (ore / giorni su un mainframe, quindi tutti dovevano condividere il compilatore). Ma quando le workstation personali sono diventate più potenti, siamo venuti tutti per accettare il processo:

  1. digita un codice senza pensare
  2. hit build / compile
  3. correggi la tua sintassi per farla compilare
  4. esegui test per vedere se ciò che hai scritto ha senso

Se pensi prima di digitare, e dopo aver digitato, rivedi ciò che hai scritto, puoi ridurre il numero di errori prima di eseguire una build e una suite di test. Impara a non premere build 50 volte al giorno, ma forse una o due volte, quindi è meno importante che il tempo di compilazione e di test richieda alcuni minuti in più.

    
risposta data 25.01.2013 - 18:03
fonte
3

Un modo possibile: dividere la tua soluzione. Se una soluzione ha 100 progetti, allora è abbastanza ingestibile. Solo perché due progetti (ad esempio A e B) utilizzano un codice comune da un altro progetto (ad esempio Lib) non significa che debbano essere nella stessa soluzione.

Invece, puoi creare la soluzione A con i progetti A e Lib e anche la soluzione B con i progetti B e Lib.

    
risposta data 25.01.2013 - 18:06
fonte
2

Sono in una situazione simile. Ho dei test unitari che testano la comunicazione con il server. Stanno testando il comportamento con timeout, annullando le connessioni, ecc. L'intera serie di test dura 7 minuti.

7 minuti è un tempo relativamente breve ma non è qualcosa che farai prima di ogni commit.

Abbiamo anche una serie di test dell'interfaccia utente automatizzati, il loro tempo di esecuzione è di 2 ore. Non è qualcosa che desideri eseguire ogni giorno sul tuo computer.

Quindi, cosa fare?

  1. La modifica dei test di solito non è molto efficace.
  2. Esegui solo i test pertinenti prima del commit.
  3. Esegui tutti i tuoi test ogni giorno (o più volte al giorno) su un server di build. Questo ti darà anche la possibilità di generare una buona copertura del codice e amp; rapporti di analisi del codice.

La cosa importante è: tutti i tuoi test dovrebbero essere eseguiti spesso perché è importante trovare i bug. Tuttavia, non è assolutamente necessario trovarli prima dei commit.

    
risposta data 25.01.2013 - 18:20
fonte
1

Anche se la tua descrizione del problema non fornisce una visione approfondita della base di codice, penso di poter dire con sicurezza che il tuo problema è duplice.

Impara a scrivere i test giusti.

Dici di avere quasi mille test e hai 120 progetti. Supponendo che nella maggior parte dei casi questi progetti siano progetti di test, hai 1000 test per 60 progetti di codice di produzione. Questo ti dà circa 16-17 test pr. progetto !!!

Questa è probabilmente la quantità di test che dovrei coprire circa 1-2 classi in un sistema di produzione. Quindi, a meno che tu non abbia solo 1-2 classi in ogni progetto (nel qual caso la struttura del tuo progetto è troppo fine) i tuoi test sono troppo grandi, coprono troppo terreno. Tu dici che questo è il primo progetto che stai facendo correttamente a TDD. A dire, i numeri che presenti indicano che questo non è il caso, non stai facendo la proprietà TDD.

Devi imparare a scrivere i test giusti, il che probabilmente significa che devi imparare come rendere il codice testabile in primo luogo. Se non riesci a trovare l'esperienza all'interno del team per farlo, ti suggerisco di assumere l'aiuto dall'esterno, ad es. sotto forma di uno o due consulenti che aiutano la tua squadra per una durata di 2-3 mesi a imparare a scrivere codice testabile e piccoli test di unità minimi.

Come confronto, sul progetto .NET a cui sto lavorando attualmente, possiamo eseguire circa 500 test unitari in meno di 10 secondi (e questo non è stato nemmeno misurato su una macchina con specifiche elevate). Se quelle fossero le tue cifre, non avresti paura di eseguirle localmente ogni tanto.

Impara a gestire la struttura del progetto.

Hai diviso la soluzione in 120 progetti. Questo è per i miei standard una quantità impressionante di progetti.

Quindi, se ha senso avere effettivamente quella quantità di progetti (che ho la sensazione che non sia così - ma la tua domanda non fornisce abbastanza informazioni per esprimere un giudizio qualificato su questo), devi dividere i progetti in componenti più piccoli che possono essere compilati, messi in versione e distribuiti separatamente. Quindi, quando uno sviluppatore esegue l'unità della suite di test, ha solo bisogno di eseguire i test relativi al componente a cui sta lavorando attualmente. Il server di build dovrebbe occuparsi di verificare che tutto si integri correttamente.

Ma la suddivisione in più parti di un progetto in più componenti, versioni e distribuzioni separate richiede, nella mia esperienza, un team di sviluppo molto maturo, una squadra più matura di quanto io abbia la sensazione che il tuo team sia.

Ma in ogni caso, devi fare qualcosa per la struttura del progetto. Separare i progetti in componenti separati o avviare la fusione dei progetti.

Chiediti se hai davvero bisogno di 120 progetti?

P.S. Potresti voler controllare NCrunch. È un plug-in di Visual Studio che esegue automaticamente il test in background.

    
risposta data 27.01.2013 - 21:11
fonte
0

I test di JUnit devono essere rapidi, ma alcuni devono semplicemente impiegare un po 'di tempo per essere eseguiti.

Ad esempio, il test del database richiede in genere un po 'di tempo per inizializzarlo e terminarlo.

Se hai centinaia di test, anche se sono veloci, richiedono molto tempo per essere eseguiti a causa del loro numero.

Cosa si può fare è:

1) Identifica i test cruciali. Quelli per le parti più importanti delle biblioteche e quelli che hanno maggiori probabilità di fallire dopo le modifiche. Solo quei test dovrebbero essere eseguiti sempre su compilazione. Se un codice è spesso rotto, i suoi test dovrebbero essere obbligatori anche se richiedono molto tempo, dall'altra parte, se parte del software non ha mai causato un problema, puoi tranquillamente saltare i test su ogni build.

2) Preparare il server di integrazione continua, che eseguirà tutti i test in background. Dipende da te se decidi di costruire ogni ora o di costruire dopo ogni commit (il secondo ha senso solo se vuoi rilevare automaticamente quale commit ha causato problemi).

    
risposta data 27.01.2013 - 18:59
fonte
0

Problemi che ho visto:

a) Utilizzo di IOC per creare elementi di test. 70 secondi - > 7 secondi rimuovendo Container.

b) Non prendere in giro tutte le classi. Mantieni i tuoi test unitari su un singolo elemento. Ho visto test che vagano attraverso un paio di classi. Questi non sono test unitari e molto più probabilità di rottura.

c) Rivolgili per scoprire cosa stava succedendo. Ho trovato che il costruttore stava costruendo cose che non mi servivano, quindi l'ho localizzato e ridotto i tempi di esecuzione.

d) Profilo. forse il codice non è buono e puoi ottenere un po 'di efficienza da una recensione.

e) Rimuovi dipendenze. Mantenere basso il test eseguibile ridurrà il tempo di caricamento. Utilizzare una libreria di interfaccia e contenitori IOC per eseguire la soluzione finale, ma i progetti di test principali devono solo definire la libreria dell'interfaccia. Ciò garantisce la separazione, assicura che sia più facile da testare e riduce anche la stampa del piede di prova.

    
risposta data 24.04.2017 - 06:16
fonte
0

Sento il tuo dolore, e ho incontrato diversi punti in cui la velocità di costruzione può essere migliorata molto. Tuttavia, il numero di cose che consiglio è quello di misurare a un dettaglio granulare per capire dove la tua build impiega più tempo. Ad esempio, ho una build con circa 30 progetti che richiede poco più di un minuto per l'esecuzione. Tuttavia, questa è solo una parte dell'immagine. So anche quali progetti impiegano più tempo per costruire, il che aiuta a focalizzare i miei sforzi.

Cose che perdono il tempo di costruzione:

  • Download di pacchetti (Nuget per C #, Maven per Java, Gem per Ruby, ecc.)
  • Copia di grandi quantità di file sul file system (esempio: file di supporto GDAL)
  • Apertura delle connessioni al database (alcuni richiedono oltre un secondo per connessione da negoziare)
  • Codice basato sulla riflessione
  • Codice generato automaticamente
  • Utilizzo delle eccezioni per controllare il flusso del programma

Le librerie fittizie usano la riflessione o iniettano il codice usando le librerie bytecode per generare la simulazione per te. Mentre è molto conveniente, mangia tempo di test. Se stai generando delle prese in giro in un loop nel tuo test, puoi aggiungere una quantità di tempo misurabile ai test delle unità.

Ci sono modi per risolvere i problemi:

  • Spostare i test che coinvolgono un database per l'integrazione (vale a dire solo sul server di creazione CI)
  • Evita di creare mock nei loop nei tuoi test. In realtà basta evitare loop nei test del tutto. Probabilmente puoi ottenere gli stessi risultati usando un test parametrizzato in questo caso.
  • Prendi in considerazione di suddividere la tua soluzione massiccia in soluzioni separate

Quando la tua soluzione contiene oltre 100 progetti, hai una combinazione di codice della libreria, test e codice dell'applicazione. Ciascuna delle librerie può essere la propria soluzione con i suoi test associati. Jet Brains Team City è un server di build CI che funge anche da server Nuget - e sono sicuro che non è l'unico . Ciò ti dà la flessibilità di spostare quelle librerie che probabilmente non vengono spesso cambiate nelle proprie soluzioni / progetti e usano Nuget per risolvere le dipendenze per il codice dell'applicazione. Soluzioni più piccole significano che puoi apportare le modifiche a una libreria in modo rapido e senza problemi e godere dei vantaggi della soluzione principale.

    
risposta data 26.04.2017 - 21:35
fonte
-1

Il tuo ambiente di test può funzionare ovunque? Se possibile, utilizza il cloud computing per eseguire i test. Dividi i test tra N macchine virtuali. Se il tempo di eseguire i test su una singola macchina è T1 secondi, il tempo per eseguirli divisi, T2, potrebbe avvicinarsi a T2 = T1 / N. (Supponendo che ogni caso di test impieghi circa lo stesso tempo). E devi solo pagare per le VM quando le stai usando. Quindi non hai un sacco di macchine di prova seduti in qualche laboratorio da qualche parte 24 ore su 24, 7 giorni su 7. (Mi piacerebbe essere in grado di farlo dove lavoro, ma siamo legati a hardware specifico. Nessuna VM per me.)

    
risposta data 26.01.2013 - 00:43
fonte

Leggi altre domande sui tag