È buona norma eseguire i test unitari nei ganci di controllo della versione?

40

Dal punto di vista tecnico è possibile aggiungere alcuni hook pre / post push che eseguiranno i test unitari prima di consentire l'unione di alcuni commit specifici al ramo predefinito remoto.

La mia domanda è: è meglio mantenere i test unitari nella pipeline di build (quindi, introdurre commesse non funzionanti al repo) oppure è meglio non permettere che si verifichino dei "cattivi" commit.

Mi rendo conto che non sono limitato a queste due opzioni. Ad esempio, posso consentire a tutti i commit di eseguire il branching e test prima di spingere il commit di merge in repo. Ma se devi scegliere esattamente tra queste due soluzioni, quale sceglierai e per quali ragioni?

    
posta shabunc 24.10.2014 - 00:39
fonte

9 risposte

33

No, non lo è, per due motivi:

Velocità

I commit dovrebbero essere veloci. Un commit che richiede 500 ms., Ad esempio, è troppo lento e incoraggerà gli sviluppatori a impegnarsi in modo più parsimonioso. Dato che su qualsiasi progetto più grande di un Hello World, avrai dozzine o centinaia di test, ci vorrà troppo tempo per eseguirli durante il pre-commit.

Naturalmente, le cose peggiorano per progetti più grandi con migliaia di test eseguiti per minuti su un'architettura distribuita, o settimane o mesi su una singola macchina.

La parte peggiore è che non c'è molto che puoi fare per renderlo più veloce. Piccoli progetti Python che hanno, ad esempio, cento test unitari, impiegano almeno un secondo per essere eseguiti su un server medio, ma spesso molto più lungo. Per un'applicazione C #, avrà una media di quattro cinque secondi, a causa del tempo di compilazione.

Da quel momento, puoi pagare $ 10.000 in più per un server migliore che ridurrà il tempo, ma non di molto, o eseguirà test su più server, il che rallenterà solo le cose.

Entrambe pagano bene quando si hanno migliaia di test (oltre a test funzionali, di sistema e di integrazione), consentendo di eseguirli in pochi minuti anziché in settimane, ma questo non è di aiuto per progetti su piccola scala.

Quello che puoi fare, invece, è:

  • Incoraggiare gli sviluppatori a eseguire test strongmente correlati al codice che hanno modificato localmente prima di eseguire un commit. Probabilmente non possono eseguire migliaia di test unitari, ma possono eseguire cinque-dieci di essi.

    Assicurati che trovare test rilevanti e eseguirli sia effettivamente facile (e veloce). Visual Studio, ad esempio, è in grado di rilevare quali test possono essere interessati dalle modifiche apportate dall'ultima esecuzione. Altri IDE / piattaforme / linguaggi / framework possono avere funzionalità simili.

  • Mantieni il commit il più velocemente possibile. Applicare le regole di stile è OK, perché spesso è l'unico posto dove farlo e perché tali controlli sono spesso sorprendentemente veloci. Fare analisi statiche è OK non appena si mantiene veloce, che è raramente il caso. Eseguire test di unità non è OK.

  • Esegui i test delle unità sul tuo server per l'integrazione continua.

  • Assicurati che gli sviluppatori siano informati automaticamente quando hanno rotto la build (o quando i test unitari hanno fallito, che è praticamente la stessa cosa se consideri un compilatore uno strumento che controlla alcuni degli errori possibili che puoi introdurre nel tuo codice).

    Ad esempio, andare a una pagina web per controllare le ultime build non è una soluzione. Dovrebbero essere informati automaticamente . Mostrare un popup o inviare un SMS sono due esempi di come potrebbero essere informati.

  • Assicurati che gli sviluppatori comprendano che interrompere la compilazione (o test di regressione falliti) non è OK, e che non appena ciò accade, la loro priorità è quella di risolverlo. Non importa se stanno lavorando su una funzione ad alta priorità che il loro capo ha chiesto di spedire per domani: hanno fallito la build, dovrebbero risolverlo.

Sicurezza

Il server che ospita il repository non deve eseguire codice personalizzato, come i test di unità, soprattutto per motivi di sicurezza. Queste ragioni sono già state spiegate nel runner CI sullo stesso server di GitLab?

Se, d'altra parte, la tua idea è quella di avviare un processo sul build server dal hook pre-commit, quindi rallenterà ancora di più i commit.

    
risposta data 24.10.2014 - 02:05
fonte
39

Lascia che sia io a essere in disaccordo con i miei compagni di risposta.

Questo è noto come Gated Check-in nel mondo TFS, e mi aspetto altrove. Quando si tenta di effettuare il check-in in una filiale con il check-in gated, il bucketet viene inviato al server, il che assicura che le modifiche vengano compilate e che i test unitari (leggi: tutti) passino. Se non lo fanno, ti avverte che sei una brutta scimmia che ha rotto la build. Se lo fanno, le modifiche passano al controllo del codice sorgente (yay!).

Nella mia esperienza, i check-in gated sono uno dei processi più importanti per il collaudo di unità di successo e, per estensione, la qualità del software.

Perché?

  • Perché i check-in con gating costringono le persone a correggere i test non funzionanti. Non appena i test non funzionanti diventano qualcosa che può fare piuttosto che deve fare, diventano de-prioritari da ingegneri pigri e / o uomini d'affari invadenti.
    • Più a lungo un test viene interrotto, più difficile (e più costoso) risolvere.
  • Perché non appena le persone devono eseguire i test anziché devono eseguire i test, l'esecuzione dei test viene aggirata da ingegneri pigri / dimenticanti e / o uomini d'affari invadenti.
  • Poiché non appena i test delle unità influiscono sul tempo di commit, le persone veramente iniziano a preoccuparsi di eseguire i test unit . La velocità conta. La riproducibilità è importante. L'affidabilità conta. L'isolamento conta.

E naturalmente, il vantaggio che hai generato in origine - quando hai i check-in con gating e una solida serie di test, ogni singolo changeset è "stabile". Si salva tutto questo sovraccarico (e potenziale errore) di "quando è stata l'ultima buona build?" - Tutte le build sono sufficienti per svilupparsi contro

Sì, ci vuole tempo per costruire ed eseguire i test. Nella mia esperienza 5-10 minuti per una buona app C # e test di 5k unità. E non mi interessa. Sì, le persone dovrebbero effettuare il check-in frequentemente. Ma dovrebbero anche aggiornare frequentemente i loro compiti, o controllare la loro posta elettronica, o prendere un caffè o dozzine di altri oggetti "non funzionanti sul codice" che costituiscono il lavoro di un ingegnere del software per occupare quel tempo. Il controllo del codice errato è molto più costoso di 5-10 minuti.

    
risposta data 24.10.2014 - 21:19
fonte
39

I commit dovrebbero essere veloci. Quando eseguo il commit di un codice, voglio che venga inviato al server. Non voglio aspettare qualche minuto mentre esegue una batteria di test. Sono responsabile di ciò che spingo al server e non ho bisogno di nessuno che mi faccia da babysitter con commit hook.

Detto questo, una volta arrivato al server, dovrebbe essere analizzato, testato e costruito immediatamente (o entro un breve lasso di tempo). Questo mi avviserebbe del fatto che i test unitari sono guasti, o non sono stati compilati, o ho fatto un casino mostrato dagli strumenti di analisi statica disponibili. Più velocemente questo è fatto (la compilazione e l'analisi), più veloce è il mio feedback e più velocemente sono in grado di risolverlo (i pensieri non sono completamente scomparsi dal mio cervello).

Quindi no, non inserire test e così via nel commit del client. Se è necessario, inserirli sul server in un commit post (poiché non si dispone di un server CI) o nel server di configurazione CI e avvisare in modo appropriato i problemi con il codice. Ma non bloccare il verificarsi di commit in primo luogo.

Devo anche sottolineare che con alcune interpretazioni di Test Driven Development, uno dovrebbe controllare un test unitario che interrompe prima . Questo dimostra e documenta che il bug è presente. Quindi un successivo check-in sarebbe il codice che corregge il test unitario. Prevenire qualsiasi check-in fino al passaggio dei test unitari ridurrebbe il valore effettivo del controllo in un test unitario che non riesce a documentare il problema.

Correlati: Devo avere dei test unitari per i difetti noti? e Qual è il valore del controllo dei test unitari guasti?

    
risposta data 24.10.2014 - 02:11
fonte
10

In linea di principio, penso che abbia senso impedire alle persone di apportare modifiche alla linea principale che interrompono la costruzione. Cioè, il processo per apportare modifiche al ramo principale del tuo repository dovrebbe richiedere di garantire che tutti i test continuino a passare. Rompere la build è semplicemente troppo costoso in termini di tempo perso per tutti gli ingegneri del progetto per fare qualsiasi altra cosa.

Tuttavia, la particolare soluzione di commit hook non è un buon piano.

  1. Lo sviluppatore deve attendere l'esecuzione dei test durante il commit. Se lo sviluppatore deve attendere sulla sua workstation affinché tutti i test vengano superati, hai sprecato tempo prezioso in ingegnere. L'ingegnere deve essere in grado di passare all'attività successiva, anche se dovrà tornare indietro perché i test hanno avuto esito negativo.
  2. Gli sviluppatori potrebbero voler commettere codice spezzato in un ramo. In un'attività più ampia, la versione di sviluppatori del codice può impiegare molto tempo non in uno stato di passaggio. Ovviamente, unire quel codice nella mainline sarebbe molto brutto. Ma è piuttosto importante che lo sviluppatore possa ancora utilizzare il controllo della versione per tracciare i suoi progressi.
  3. Ci sono occasionalmente buoni motivi per saltare il processo e bypassare i test.
risposta data 24.10.2014 - 05:07
fonte
3

No, non dovresti farlo come hanno indicato altre risposte.

Se si desidera avere un codice base garantito senza test non validi, è possibile invece sviluppare su rami di funzionalità e eseguire il pull delle richieste in master. Quindi puoi definire i presupposti per accettare quelle richieste di pull. Il vantaggio è che puoi spingere molto velocemente e i test vengono eseguiti in background.

    
risposta data 24.10.2014 - 08:10
fonte
2

Dovendo aspettare per la build di successo e test su ogni commit al ramo principale è davvero terribile, penso che tutti siano d'accordo su questo.

Ma ci sono altri modi per ottenere un ramo principale coerente. Ecco un suggerimento, un po 'simile nella vena di check-in gated in TFS, ma che è generalizzabile a qualsiasi sistema di controllo di versione con rami, anche se per lo più userò termini git:

  • Avere un ramo di staging in cui è possibile eseguire il commit solo delle unioni tra i rami dev e il ramo principale

  • Imposta un hook che avvia o accoda una build e verifica i commit eseguiti sul ramo di staging, ma ciò non fa aspettare il committer

  • In caso di build e test riusciti, fai in modo che il ramo principale vada in avanti se è aggiornato

    Nota: non eseguire automaticamente unisci nel ramo principale, perché l'unione verificata, se non un'unione in avanti dal punto di vista del ramo principale, potrebbe non riuscire una volta unita al ramo principale con commit tra

Di conseguenza:

  • Vieta l'impegno umano al ramo principale, automaticamente se puoi, ma anche come parte del processo ufficiale se c'è una scappatoia o se tecnicamente non è fattibile per far rispettare questo

    Almeno, puoi essere sicuro che nessuno lo farà involontariamente o senza malizia, una volta che è una regola fondamentale. Non dovresti tentare di farlo, mai.

  • Dovrai scegliere tra:

    • Un singolo ramo di staging, che farà in modo che l'unione abbia successo, fallisce in realtà se fallisce una fusione precedente non ancora costruita e non testata

      Almeno, saprai quale unione è fallita e chiedi a qualcuno di correggerla, ma le unioni intermedie non sono banalmente tracciabili (dal sistema di controllo della versione) per i risultati di ulteriori build e test.

      Puoi osservare l'annotazione del file (o incolpare), ma a volte una modifica in un file (ad esempio la configurazione) genererà errori in luoghi imprevisti. Tuttavia, questo è un evento piuttosto raro.

    • Più rami di staging, che permetteranno alle unioni non conflittuali riuscite di raggiungere il ramo principale

      Anche nel caso in cui qualche altro ramo di gestione temporanea abbia una fusione non in conflitto . La tracciabilità è un po 'meglio, almeno nel caso in cui una fusione non si aspettasse una modifica che influisce da un'altra fusione. Ma ancora una volta, questo è abbastanza raro da non preoccuparsi di tutti i giorni o di tutte le settimane.

      Per avere fusioni non conflittuali il più delle volte, è importante dividere sensibilmente i rami di staging, ad es. per squadra, per livello o per componente / progetto / sistema / soluzione (non è il caso di chiamarlo).

      Se nel frattempo il ramo principale è stato inoltrato a un'altra fusione, è necessario unire nuovamente. Si spera che questo non sia un problema con le fusioni non conflittuali o con pochissimi conflitti.

In confronto ai check-in con gating, il vantaggio qui è che hai una garanzia di un ramo principale funzionante, perché il ramo principale può solo andare avanti, non per unire automaticamente le tue modifiche con qualunque cosa sia stata commessa in mezzo. Quindi il terzo punto è la differenza essenziale.

    
risposta data 25.10.2014 - 17:51
fonte
2

Preferisco che i "test unitari passati" siano una porta per l'invio del codice. Tuttavia, per fare in modo che funzioni, avrai bisogno di alcune cose.

Avrai bisogno di un framework di build che memorizzi gli artefatti.

Avrai bisogno di un framework di test che memorizza nella cache lo stato del test da (con successo) esecuzioni di test, con qualsiasi dato artefatto (i).

In questo modo, i check-in con i test delle unità che passano saranno rapidi (controllo incrociato dalla fonte al manufatto che è stato costruito quando lo sviluppatore ha controllato i test prima del check-in), quelli con test dell'unità guasti saranno bloccati e gli sviluppatori che si dimenticano opportunamente di controllare i build prima del commit saranno incoraggiati a ricordarsi di farlo la prossima volta, perché il ciclo di compilazione e test è lungo.

    
risposta data 27.10.2015 - 14:03
fonte
1

Direi che dipende dal progetto e dall'ambito dei test automatici che vengono eseguiti sul "commit".

Se i test che desideri eseguire nel trigger del check-in sono molto veloci, o se il flusso di lavoro dello sviluppatore forzerà un po 'di lavoro amministrativo dopo tale check-in in ogni caso, penso che non dovrebbe importare molto e forzare gli sviluppatori per controllare solo cose che eseguono assolutamente i test di base. (Suppongo che tu eseguiresti solo i test di base su un simile trigger).

E penso che, permettendo la velocità / il flusso di lavoro, è una buona cosa non spingere le modifiche ad altri sviluppatori che non superano i test - e sai solo se falliscono se li esegui.

Scrivete "commit ... to remote branch" nella domanda, il che significherebbe che questo è (a) non qualcosa che un dev fa ogni pochi minuti, quindi una piccola attesa può essere molto ben accettabile e (b) che dopo un tale commit le modifiche al codice possono avere un impatto su altri sviluppatori, quindi potrebbero essere necessari ulteriori controlli.

Posso essere d'accordo con le altre risposte su "non fare in modo che i tuoi sviluppatori si divertano un po 'a guardare" per un'operazione del genere.

    
risposta data 24.10.2014 - 21:13
fonte
1

I commit interrotti non dovrebbero essere consentiti su trunk , perché trunk è ciò che può andare in produzione. Quindi devi assicurarti che ci sia un gateway che devono passare prima di trovarsi su trunk . Tuttavia, i commit errati possono essere completamente validi in un repository, purché non siano sul trunk.

D'altra parte, richiedere agli sviluppatori di attendere / correggere i problemi prima di spingere le modifiche al repository ha una serie di inconvenienti.

Alcuni esempi:

  • In TDD, è comune eseguire il commit e spingere i test in errore per le nuove funzionalità prima di iniziare a implementare la funzione
  • Lo stesso vale per la segnalazione di bug commettendo e spingendo un test fallito
  • La trasmissione di codice incompleto consente a 2 o più persone di lavorare su una funzione in parallelo facilmente
  • L'infrastruttura CI può richiedere tempo per la verifica, ma lo sviluppatore non deve attendere
risposta data 26.07.2018 - 12:31
fonte

Leggi altre domande sui tag