Che livello mi prendo in giro quando ho una funzione? Una funzione di chiamata B che chiama il database?

4

Ho creato uno scenario in cui ho due funzioni, A e B, entrambe sono testate, ma nonostante i test ho comunque iniettato un bug piuttosto stupido e alcuni dati non entrano nel database. Si noti che non è presente ORM. Ho scritto le mie operazioni CRUD direttamente, e sto pagando il prezzo per questo.

In questo scenario, ho (perl) qualcosa del genere:

sub A {
  ... do a bunch of work ...
  my $res = B( %data );
  ... do a bunch more work ...
}

sub B {
  my ( %data ) = @_;
  ... record data to the database and return a result ...
}

Ciò che è successo, però, è che nella mia applicazione di livello superiore e in A stesso, ho cambiato il nome di uno dei campi che va in% dati, ma ho dimenticato di cambiarlo in B. Ho un test per A quale stub / prende in giro un lotto delle cose che A fa (A deve soddisfare un buon refactoring), incluso B. Lo stub per B verifica che tutti i parametri corretti, compresi i nomi dei campi corretti passare in, e così passa il test.

MA il test per B verifica i vecchi nomi di campo, la vera B usa effettivamente i vecchi nomi di campo e anche quel test passa.

E, quindi, ho messo in produzione del codice che, a causa di questa svista, sta inserendo NULL nel database in cui non desidero i NULL.

  • A è chiaramente troppo grande. Per isolarlo, sto prendendo in giro sei diverse funzioni, ognuna delle quali potrebbe causare un cambiamento di stato del sistema.
  • A, e quindi il test per A è teoricamente indifferente alla strategia con cui B memorizzerà i dati nel database o anche a quale tipo di database B memorizzerà i dati, quindi penso che il test per A probabilmente non dovrebbe interrogare il database direttamente.
  • Ma ho cambiato il formato di% di dati e penso che il test per A avrebbe dovuto rilevare che sostanzialmente ho cambiato l'API senza propagare la modifica.

Quale è un modo migliore per impostare un test per rilevare questo caso in anticipo?

  • Continua con la strategia di isolamento per A e B, ma scrivi anche un'integrazione che testa A - alla maniera di > il database? Ciò porterebbe a una proliferazione di possibili casi di test, e sto già avendo difficoltà a giustificare la gestione del caso di proliferazione al caso (anche se so che la copertura è dolorosamente inadeguata).
  • Basta scrivere il test per A per interrogare il database, possibilmente attraverso un'astrazione?
  • C'è qualcosa di diverso che potrebbe aiutarti qui?

In definitiva, ho seguito il suggerimento di Karl Bielefeldt. Ho aggiunto il codice di convalida dei dati nella funzione B. Ho anche aggiunto il test di integrazione che controlla da A fino al DB.

Per quanto posso dire, ho bisogno di avere quanti più test di integrazione possibili, verificando che lo stato generale delle risorse di sistema cambi in modo prevedibile. Allo stesso tempo, prevedo che avrò ancora bisogno di prendere in giro le risorse di rete se non riesco a ottenere un ambiente di rete di test.

    
posta Savanni D'Gerinel 06.08.2012 - 19:33
fonte

4 risposte

5

Non tutti i problemi possono essere risolti con il test della scatola nera. Il problema con i test di integrazione end-to-end è che il numero di possibili percorsi attraverso il codice è molto più grande rispetto al test dell'unità. È ancora buono avere test di integrazione, ma è generalmente impossibile renderli completi a meno che il tuo sistema non sia molto semplice. Pertanto, è probabile che il prossimo errore di integrazione che hai non sarà coperto da un test a meno che tu non l'abbia visto prima.

La mia raccomandazione è di aggiungere alcuni test di integrazione per assicurarti di non ripetere ancora lo stesso errore, ma anche di aggiungere alcuni validazione all'interno % diB e di fare test di unità per quella convalida (vedi asserzione per maggiori informazioni). Verifica che non sia passato in nessun campo di cui non sia a conoscenza, che tutti i campi obbligatori siano passati, ecc. Se la validazione influisce negativamente sul rendimento, puoi avere un flag per disattivarli. Quindi, invece di testare il bug, lo sviluppatore che lo presenta lo vedrà nelle sue esecuzioni di test.

    
risposta data 06.08.2012 - 20:04
fonte
1

Continue with the isolation strategy for A and B, but also write an integration that tests A --all the way to-> the database? This would lead to a proliferation of possible test cases, and I'm already having trouble justifying the existing test case proliferation to management (though I know the coverage to be woefully inadequate).

Questa è la soluzione.

Avere questo bug dovrebbe fornire più foraggio per la gestione. "Se avessimo avuto test di integrazione, questo non sarebbe mai stato un bug."

    
risposta data 06.08.2012 - 19:42
fonte
1

Ci sono molte cose che dovresti fare:

  • refactoring del codice. Rendi le tue funzioni A e B più piccole, con meno funzionalità e piccoli pezzi di test unitario
  • aggiungi i test di integrazione mancanti. Ovviamente hai perso alcuni test, dal momento che è stato possibile attivare un bug.
  • aggiungi alcuni controlli di runtime. Se determinate condizioni non vengono soddisfatte, genera un'eccezione.
risposta data 06.08.2012 - 21:00
fonte
0

The stub for B verifies that all of the correct parameters, including the correct field names get passed in, and so the test passes.

La tua migliore scommessa per evitare qualcosa di simile è entrare nell'abitudine del TDD. Se lo avessi fatto, ti saresti reso conto di aver bisogno di un test per fallire prima di apportare le modifiche. Quel test (che si tratti di un nuovo test o di un'estensione di un test esistente) avrebbe controllato i tuoi nuovi campi.

    
risposta data 06.08.2012 - 21:23
fonte

Leggi altre domande sui tag