Alternativa all'indicatore "Passing / Broken build"?


14

Quando si ha un'integrazione continua che esegue i test ad ogni commit, una best practice comune è far passare tutti i test in qualsiasi momento (ovvero "non interrompere la build").

Trovo alcuni problemi con questo:

Ad esempio, non è possibile aiutare un progetto open source creando test corrispondenti ai ticket. So che se propongo una richiesta pull a un progetto open source contenente un test non riuscito, la build verrà contrassegnata come non riuscita e il progetto non vorrà che venga unito nel suo repository perché "interromperà la build".

E non credo sia una brutta cosa avere test falliti nel tuo repository , è come avere problemi aperti nel tuo tracker. Queste sono solo cose che aspettano di essere risolte.

Lo stesso vale in un'azienda. Se si lavora con TDD, non è possibile scrivere test, eseguire il commit e quindi scrivere il codice logico che soddisfa il test. Ciò significa che se ho scritto 4-5 test sul mio laptop, non posso impegnarli prima di andare in vacanza. Nessuno può riprendere il mio lavoro. Non riesco nemmeno a "condividerli" con un collega se non inviandoli ad esempio via e-mail. Impedisce inoltre di lavorare con una persona che scrive i test, l'altra che scrive il modello.

Tutto questo per dire: sto abusando / fraintendendo il processo di costruzione / integrazione continua? Mi sembra che "passare" / "non passare" sia un indicatore troppo stretto.

C'è un modo per rendere compatibile l'integrazione continua e TDD?

Forse esiste una soluzione / pratica standard per distinguere "nuovi test" (che possono fallire) e "test di regressione" (che non dovrebbero fallire perché erano soliti funzionare)?


1
Avere un indicatore che mostra se il numero di test falliti è salito (rosso) o giù (verde) nell'ultima ora (o giù di lì).
Joachim Sauer

2
Non sono uno specialista di TDD / ALM (da qui il commento, piuttosto che una risposta) ma penso che il tuo problema possa essere risolto con rami privati ​​/ rami di funzionalità. Stai lavorando alla funzione A? Diramalo, lavora sul ramo (con i colleghi) e, una volta terminato, uniscilo nel tronco continuamente integrato.
Avner Shahar-Kashtan,

@JoachimSauer Sì, ma tale metrica è standardizzata / utilizzata in qualsiasi grande progetto? Sto cercando di capire perché la maggior parte dei progetti (e strumenti CI) funzionano in questo modo.
Matthieu Napoli,

Penso che il criterio corretto per "test che possono fallire" non sia "nuovi test", ma piuttosto "test per problemi aperti noti". Posso vedere come questi test sono utili da avere - Posso anche come quei test NON sono utili nella build CI perché inquinano il significato del test pass / falliscono lì (vuoi solo eseguire test per i quali qualcuno ha effettivamente trascorso del tempo per farli passare).
Joris Timmermans,

@MadKeithV Esatto
Matthieu Napoli

Risposte:


12

Vedo dove stai arrivando, ma questi tipi di problemi sono in genere risolti in altri modi. C'è una buona ragione per cui questo è un protocollo standard. Se qualcuno invia codice che non viene compilato, tutti gli utenti che aggiornano il proprio codice avranno un programma che non viene compilato . Ciò include i programmatori che stanno attualmente lavorando a qualcosa di completamente diverso e in qualche modo si trovano in una situazione in cui devono attendere prima di poter compilare e testare ciò su cui stanno lavorando.

Il protocollo standard prevede che sia possibile eseguire il commit delle modifiche anche per un lavoro completo o addirittura incompleto, purché venga compilato in modo che i programmatori possano aggiornare il proprio codice ogni giorno, se necessario.

Tuttavia, vedo ancora a cosa stai arrivando. A volte vuoi impegnarti per salvare semplicemente il tuo codice. Per questo, la maggior parte dei repository di origine supporta la ramificazione. Ciò ti consente di creare un ramo privato, lavorarci su senza disturbare gli altri, quindi unirti al tronco quando il lavoro è completato. Ciò consente di eseguire il commit quando lo si desidera senza alcuna contraccolpo associato alla rottura della build.

Se ciò non è adatto, GIT ti consente di eseguire il commit (push) nei repository sul tuo computer locale, ma è possibile che il repository sia ovunque. È possibile creare un repository per un lavoro potenzialmente parziale / incompleto e un altro repository per il lavoro finito e su quel repository è possibile aggiungere una build notturna.

Ancora una volta, non posso sottolineare l'importanza abbastanza. Non commettere mai codice rotto su trunk! I tuoi contributi non possono influire sul lavoro di altri programmatori.

modificare

Vedo che avevi intenzione di eseguire prove fallite, ma secondo la mia modesta opinione, c'è poca differenza. Lo scopo di un test è determinare se un particolare aspetto di un programma passa o fallisce. Se fallisce sempre e non fai nulla, il test, nell'uso tradizionale del test unitario, non serve a nulla. Se lo usi per eseguire qualche altra metrica che non implica necessariamente un commit "fallito" se uno di questi test fallisce, ti consiglio caldamente di trovare un altro modo di fare la stessa cosa.

Altrimenti si rischia che il test non venga mai preso in considerazione o che causi il fallimento della build, che i colleghi programmatori ignorino le build fallite. È più importante che i programmatori si rendano conto quando hanno rotto una build piuttosto che eseguire un test che non offre informazioni reali e può solo comportare cattive pratiche.


1
Anzi, fai un punto con i rami dell'argomento. Ma non sto mai parlando di commettere un codice non funzionante, solo di fallire i test. Ad esempio, potrei aiutare un progetto open source creando test per i biglietti in entrata anche se non so come risolverli. Ciò consente di risparmiare un po 'di tempo per i manutentori.
Matthieu Napoli,

Se sto lavorando allo stesso tuo progetto e carichi un test non riuscito, ora ho una build con un test fallito. Potrei finire per eliminare il tuo test, dal momento che quella funzionalità non è ancora stata implementata, o decidere di implementare la funzione e finire per calpestare il tuo codice e perdere tempo. Se ci fosse una cultura nel fare questo, quel tipo di risposta potrebbe essere evitato, ma poi tutti lo farebbero, e così anche quando tutti i test superano, non tutti i miei lo fanno. In tal caso, la build avrebbe sempre test non riusciti. Non vedo un lato positivo.
Michael Shaw,

the build would always have failing testsprecisamente! Ma è una cosa così brutta? La nostra unica metrica è "la build è rotta o no", ma il tuo codice potrebbe essere pieno di bug noti , quindi ciò non significa nulla se non che non c'è regressione. In un mondo perfetto, ogni problema con il tracker dovrebbe essere testato (la riproduzione è più semplice della correzione). Quindi il lato positivo sarebbe vedere che 35 test / 70% di tutti i test stanno superando, che Branch-A lo migliora a 40 test (80%) senza regressione e che Branch-B ha regressioni. Oggi puoi solo dire che Master e Branch-A sono OK e Branch-B è rotto.
Matthieu Napoli,

@Matthieu Vedo a cosa stai arrivando. Sembra che tu abbia bisogno di una categoria speciale o di qualcosa che dice "hey, se questo test fallisce, va bene. Lo sappiamo. Ma vogliamo ancora che venga eseguito e se passa allora è ancora meglio e dovremmo rimuovere la categoria speciale perché ora ci importa se si rompe "
Earlz

@Earlz Exactly! Quello che mi chiedo è "è fatto da qualcuno, da qualche parte? E ci sono strumenti che lo supportano (librerie di test CI e unità?" Perché se avessi categorizzato quei test con strumenti classici di CI e unit test, la build sarà sempre fallisco comunque e non vedrò alcuna differenza tra i test falliti, e quindi non sarà utile: /
Matthieu Napoli

4

Dato un ramo principale con test falliti, come puoi essere sicuro - senza confrontare tale elenco con le build precedenti - che non hai introdotto bug?

Il semplice monitoraggio del numero di test non riusciti è insufficiente: è possibile correggere un test e interromperne un altro. E se sei in vacanza, non sarà chiaro agli altri guardare alla build fallita.

Mantieni sempre pulito e verde il tuo ramo principale . Lavora in un ramo. Mantieni la filiale in CI, in un lavoro separato, e fai dei test falliti sul contenuto del tuo cuore. Basta non rompere il maestro.

Chiedi al revisore della filiale di unire la tua filiale solo se supera tutti i test. (Più fortemente: avere il revisore in grado di unire il proprio ramo solo se il risultato della fusione del ramo in master supera tutti i test!)


2
Simply tracking the number of failing tests is insufficientquesta non è l'unica metrica possibile. Ad esempio: Branch-A improves it to 40 tests (80% passing) with no regression. Nessuna regressione significa che i test precedentemente superati passano sempre. In breve, un test potrebbe fallire fintanto che non è mai passato. Mi sembra che ci manchino le cose buone limitandoci a proibire i test falliti nei rami principali. (ovviamente richiederebbe strumenti che funzionino diversamente: unit test, CI, ...)
Matthieu Napoli

Sono ancora al mio punto: il maestro dovrebbe essere sempre verde, perché è chiaro e inequivocabile. Certamente i test dei marker falliscono ... in un ramo di caratteristiche. L'IC può continuare a ricordare alle persone bug eccezionali.
Frank Shearar,

Penso che ciò che Matthieu propone sia una definizione leggermente diversa di "verde", che non si discosti dal padrone essendo sempre verde. Non è ovvio per me che non abbia senso - avrebbe bisogno di alcuni strumenti non del tutto banali per il monitoraggio, ovviamente. (Hai bisogno di annullare una modifica che ha fatto passare quel test? Fortuna se questo significa che lo stato è improvvisamente rosso ...)
Christopher Creutzig

NUnit ha il concetto di un test ignorato. Potrebbe essere un'alternativa: non vengono eseguiti quindi non falliscono, ma vengono comunque segnalati come ignorati.
Frank Shearar,

2

Esistono modi per risolvere i tuoi problemi senza gettare via pratiche ben comprese e accettate sull'integrazione continua.

Inizierò con il problema di eseguire un "test interrotto" che corrisponde a un biglietto. Una soluzione è quella di creare uno o più test di rottura che espongono il problema, quindi risolverlo effettivamente , in modo che possano essere uniti nuovamente alla riga di codice principale. La seconda soluzione è quella di avere i test interrotti, ma utilizzare un tipo di flag ignore in modo che non vengano effettivamente eseguiti e interrotti la generazione. Eventualmente aggiungere un commento o un'annotazione speciale che renda molto ovvio che si tratta di un test non funzionante Ticket#N. Allegare anche una nota al ticket stesso che si riferisce ai test creati che sono in attesa di essere non firmati ed eseguiti . Ciò aiuterebbe una persona a sistemare il biglietto, ma non sarebbe anche una bandiera rossa per chi incontra il test.

E sul tuo prossimo problema con TDD. TDD consiste nel scrivere un piccolo test e poi scrivere un piccolo pezzo di codice per far passare quel test . Quindi continua a ripetere fino a quando non hai un piccolo modulo funzionale. Sento che se scrivi 4-5 test, poi vai in vacanza, potresti sbagliare. È possibile associare il programma a qualcuno in modo che uno di voi scriva il test, l'altro il codice corrispondente. Tuttavia, non è necessario utilizzare il repository della riga di codice principale per condividere questo codice tra di voi due prima che un modulo completato sia pronto per il commit. Come altri hanno suggerito, un ramo condiviso risolverebbe i tuoi problemi lì.

Cercare di rompere il mantra dell'integrazione continua può portare a percorsi imprevisti e spaventosi. Ad esempio, cosa significherebbe la copertura del codice in questo tipo di ambiente ? Come potrebbero gli sviluppatori non ritenere che il sistema abbia molte " finestre rotte " ? Come si potrebbe fare un cambiamento, eseguire il test e sapere se stanno effettivamente rompendo qualcosa di nuovo o è solo roba vecchia?


Non hai bisogno di alcun attrezzo per condividere con la persona con cui stai accoppiando la programmazione - passa semplicemente la tastiera. Se stai usando computer diversi, beh, non c'è niente di sbagliato in questo, non è semplicemente "accoppia programmazione".
Christopher Creutzig,

1

Penso che il tuo problema fondamentale sia che includi i RISULTATI del test come parte della build. Mentre ovviamente alcune persone sono d'accordo con te, altre no. La rottura della build si verifica quando non viene creata. Non quando non si costruisce senza errori.

Prendi in considerazione un grande progetto come Windows o Linux, o anche qualcosa come Firefox - pensi che vengano spediti senza bug? Ovviamente no. Ora questi progetti non stanno facendo TDD, ma questo è davvero irrilevante - TDD non cambia due fatti fondamentali: i bug esistono e ci vuole tempo per risolverli. Tempo che un progetto (open source o no) non può permettersi di perdere in bug a bassa priorità. KDE ha recentemente corretto un bug che era stato risolto da oltre un decennio. Quando è stata l'ultima volta che hai sentito qualcuno dire "Sono contento che abbiamo aspettato un decennio per spedire il nostro progetto"?

TDD, in un certo senso, probabilmente rende più facile la spedizione con bug, perché hai una migliore comprensione di quale sia il difetto. Se riesci a definire con precisione le cause del bug, hai una base eccellente per valutare il costo per risolverlo.

La mia raccomandazione è di trovare un progetto a cui non dispiaccia un po 'di rosso tra il verde.


1
 > a common best practice is to have all the tests passing (green) at all times.

Preferisco che tutti i test non falliscano (non rossi).

Con questa definizione leggermente diversa puoi anche definire i test che lo sono

  • non ancora implementato (grigio in nunit se è presente NotImplementedException)
  • noto per essere fallito = "deve fare" contrassegnando / annotando il test come ignorato (giallo)

Se li controlli nel repository la tua build continua non è rotta e quindi valida.


0

È possibile prendere in considerazione due diversi "concetti" di creazione di elementi della configurazione.

  1. Build CI regolari. La normale compilazione di elementi della configurazione dovrebbe compilare ed eseguire solo i test per i quali è stato scritto il codice per farli passare, in modo che i report degli elementi della configurazione relativi al superamento / esito negativo del test siano un indicatore chiaro e inequivocabile di regressione rispetto allo stato del codice precedentemente accettato.
  2. Una build CI "futura". Questa build compila ed esegue solo quei test per i quali non è stato scritto alcun codice specifico per farli passare. Ci possono essere diversi motivi per fare un simile test:

    • I test possono essere aggiunti per casi di errore specifici dal tracker dei problemi, per i quali non è stata ancora tentata alcuna correzione. È chiaramente utile avere già un test codificato ed in esecuzione per un problema anche senza una correzione.

    • Test aggiunti per le nuove funzionalità richieste che non sono ancora state implementate.

    • Spesso è più facile sapere come verificare un errore o una funzionalità piuttosto che sapere come implementare la funzione o la correzione e separare i due passaggi eseguendo il test sul controllo del codice sorgente può essere utile per garantire che nessuna informazione vada persa.

Come in CI "standard", nel normale regime di sviluppo il team avrebbe esaminato solo i risultati giornalieri della build normale.

Il team può anche tenere d'occhio l'evoluzione dei casi di test dalla build "futura" di elementi della configurazione, in particolare per vedere se eventuali modifiche apportate per la normale scheda tecnica risolvono effettivamente i problemi della build "futura", che può essere un'indicazione di un importante problema di fondo o miglioramento del design.

Infine, se un membro del team ha più tempo a disposizione, potrebbe dare un'occhiata alla risoluzione di uno dei problemi da "futuro" e se riesce a migrarlo su "normale" (durante l'aggiornamento dello stato del tracker dei problemi).


0

E non credo sia una brutta cosa avere test falliti nel tuo repository, è come avere problemi aperti nel tuo tracker. Queste sono solo cose che aspettano di essere risolte.

Il problema non sta fallendo i test, è un semplice indicatore del contesto di regressione. Alka: come sviluppatore, posso controllare un singolo indicatore e sapere se ho introdotto un codice di regressione o di rottura.

Nel momento in cui si introduce il concetto di guasti "soft" (va bene, ci stiamo lavorando / non ancora implementato / stiamo aspettando la nuova versione / passerà di nuovo una volta che questa altra build è stata risolta), è necessario chiunque possa eseguire o guardare il test per conoscere lo stato previsto. Che in una squadra abbastanza grande cambierà di ora in ora: il tuo indicatore diventa insignificante. In un contesto più piccolo (test di integrazione privata del team, ad esempio), penso che sia come un debito tecnico ed è ok - deve solo essere gestito.

Il modo in cui la maggior parte dell'indirizzo dello strumento è "verde / passante" riflette il risultato atteso, non che il codice funzioni:

  • Il fitness ha il concetto di fallimento previsto.
  • JUnit ha @Ignored / @Test (prevedono =)
  • Il cetriolo ha lo stato "non ancora implementato" (o come si chiama)

Questi hanno i loro problemi (come si fa a distinguere tra "sì, sappiamo che è rotto, lavorando su di esso" e "il comportamento corretto è un'eccezione") - ma aiutano.


0

Uso i test saltati.

Nel particolare framework di unit test che utilizzo, posso sollevare un'eccezione SkipTest. Il test non viene effettivamente eseguito e il suo errore non interromperà la generazione. Tuttavia, posso vedere il numero di test saltati e vedere se c'è da fare in quella zona.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.