Test del rivelatore di modifiche considerati dannosi

6

Ho letto questo articolo dal blog di test di google . Fondamentalmente, l'idea espressa nell'articolo è che è una buona pratica evitare di scrivere "test del rilevatore di modifiche". Questo codice è fornito come esempio per evitare

// Production code:
def process(w: Work)
  firstPart.process(w)
  secondPart.process(w)

// Test code:
part1 = mock(FirstPart)
part2 = mock(SecondPart)
w = Work()
Processor(part1, part2).process(w)
verify_in_order
  was_called part1.process(w)
  was_called part2.process(w)

Per essere onesti, non capisco davvero perché questo è un test negativo. Il mio approccio sarebbe quello di testare le unità part1.process, part2.process e poi fare esattamente questo test. Quindi, con il tempo, se il comportamento di questo metodo viene modificato in modo tale che i metodi non vengono chiamati come previsto (diciamo aggiungiamo una condizione a seconda della prima chiamata al metodo), il test fallirà e dovrò accertarmi che il cambiamento di comportamento è previsto e desiderato.

Che cosa sto sbagliando qui?

    
posta Phil-R 20.03.2017 - 21:52
fonte

3 risposte

6

Il frammento di codice che hai mostrato è un eccellente test unitario , a condizione che l'unità sotto test sia un combinatore di parti astratte e che non abbia una responsabilità più concreta all'interno del dominio problematico dell'applicazione.

Ad esempio, supponiamo che il punto effettivo di questo pezzo di codice sia quello di calcolare un tasso di interesse. Come dettaglio di implementazione, questo calcolo è stato diviso in due parti. Un test che non riguarda i tassi di interesse è quindi un test inutile. Il test mostrato passerebbe indipendentemente dal fatto che il calcolo sia corretto. Non garantisce che il sistema si comporti come previsto, ma asserisce che il sistema è implementato in un modo particolare. In generale, tali test non sono una buona idea. Sono fragili e si romperanno ogni volta che il sistema verrà refactored. Il post del blog ha ragione nell'affermare che tali test possono causare più lavoro di quanto valga.

Tuttavia, il codice viene presentato senza contesto e potrebbe essere l'intero punto di quel codice per combinare in sequenza tali processori. Tale codice potrebbe far parte di una libreria di motori di regole astratte o, più in generale, come parte di un Pattern composito. È quindi assolutamente corretto verificare che i due processori siano stati richiamati e richiamati nell'ordine corretto. Ho scritto questo codice e ho scritto tali test senza una cattiva coscienza.

Per questo test è del tutto ragionevole utilizzare oggetti finti, poiché il comportamento concreto delle parti del processore è al di fuori dell'ambito dell'unità sottoposta a test. Secondo il libro Quality Code di Stephen Vance: "Un unit test è un test che verifica il valore aggiunto dal codice sotto test. Qualsiasi utilizzo di collaboratori testabili indipendentemente è semplicemente una questione di convenienza "(Vance 2013, p 26). Qui, il valore aggiunto dal codice combina due processori in uno. È quindi sufficiente verificare ciò che fa questo codice (concatenando due sub-processori), e non è necessario testare ciò che fanno questi collaboratori (ad esempio che i sub-processori eseguono un calcolo del tasso di interesse corretto). Tali test sono anche utili, ma sarebbero test di integrazione.

Quindi, in entrambi i casi, la domanda da porre è: qual è il punto di questo codice, perché esiste? Cosa rappresenta questa astrazione? Quale interfaccia offre ai chiamanti? Quale comportamento è documentato? Quindi prova il suo comportamento e non il meccanismo attraverso il quale è implementato.

    
risposta data 22.03.2017 - 14:07
fonte
6

Non sono sorpreso che tu sia confuso, il Blog è chiaro come il fango del vero problema.

Per essere considerato un test unitario utile e significativo, il codice di test dovrebbe testare l'obiettivo e non l'implementazione. Nell'esempio, il codice di test verifica che gli oggetti Mock siano stati chiamati e non che abbiano funzionato come previsto e che l'obiettivo sia stato raggiunto.

Supponiamo che lo scopo di process sia di trasformare "w" in "w12", il requisito per partOne di aggiungere "1" e partTwo per aggiungere il "2" . Il test in realtà non prova che l'obiettivo del test è stato raggiunto.

    
risposta data 20.03.2017 - 22:15
fonte
1

L'ordine delle chiamate è un dettaglio di implementazione. Se devono assolutamente accadere in questo ordine per ogni chiamante, per sempre, sembra che la tua astrazione sia stata interrotta.

    
risposta data 20.03.2017 - 22:16
fonte

Leggi altre domande sui tag