Come dovrei eseguire un test unitario su una classe che si basa sull'avere dati realistici?

8

Ho una classe che incapsula i risultati di una misurazione scientifica. Sto costruendo test unitari sin dall'inizio, ma non ho molta esperienza con i test unitari e non sono sicuro di quali comportamenti dovrei testare e come.

La mia classe fa tre tipi di cose:

  1. Legge i dati di misurazione da un file (o una stringa) nelle sue variabili di istanza
  2. Scrive i suoi dati di misurazione su un file o una stringa
  3. Esegue calcoli sui suoi dati (ad esempio ottenendo la media di un insieme di numeri)

Il mio approccio al momento è quello di includere un file di dati di esempio noto nella mia directory test . Un test legge i dati dal file, li passa alla mia classe e si assicura che soddisfi alcuni controlli di integrità di base. Un altro test passa il nomefile del file alla mia classe, consente alla classe di leggerlo e esegue gli stessi test. Il resto dei test legge i dati dal file, lo passa alla mia classe e controlla che i risultati dei metodi di elaborazione dati siano corretti, dato quello che so di quel set di dati.

Questo sembra piuttosto intricato, però. I test che verificano (3) presuppongono implicitamente che i comportamenti di (1) siano corretti, dal momento che sono le funzioni in (1) che vengono utilizzate per popolare la classe in primo luogo. E i test di (1) potrebbero beneficiare degli ampi controlli effettuati dai test per (3). Sto strutturando male i miei test unitari o è solo un risultato naturale del fatto che ho bisogno di utilizzare un set di dati specifico nei miei test?

    
posta bdesham 29.08.2013 - 23:20
fonte

4 risposte

13

Quello che stai facendo è un test di integrazione, perché come probabilmente puoi dire, i tuoi test hanno dipendenze da altre parti del tuo codice. Questo va bene, ma è bene sapere quando stai guardando articoli / esempi / ecc. in linea.

Alcuni punti da considerare e cose da ricordare:

  • Disponi , Agisci , Assert . Tutti i test hanno questi 3 passaggi.
    • Hai bisogno di più passaggi? Probabilmente stai testando troppo per 1 test
    • No Disponi? Hai dipendenze esterne, che quasi sempre rendono i test instabili e inaffidabili.
    • Un sacco di codice Arrange di solito significa molte dipendenze e tipi di dipendenze dello stato del sistema. Questa è una ricetta per il codice fragile.
    • Nessuna legge? Prova spesso il compilatore. Lascia questo agli sviluppatori del compilatore.
    • Un sacco di atto (i)? Probabilmente stai testando troppo per 1 test di nuovo.
    • No Assert? Spesso accade quando si vuole verificare che "non si siano verificati errori". Nessun errore! = Funziona correttamente.
    • Un sacco di asserzioni? Il codice in prova potrebbe fare troppo / toccare troppi sistemi. Prova a refactoring e testare invece i singoli bit.
  • I test dovrebbero esistere da soli. Evita scenari in cui qualcosa che testa 3 dipende dal codice che 1 . Invece, cerca di sostituire il codice che 1 con codice di test. Nel tuo scenario: prova a impostare manualmente i valori su ciò che desideri siano nel passaggio Disponi del test, quindi esegui lo Act (i calcoli), quindi Segnala il risultato.
  • I test del percorso felice sono spesso una perdita di tempo. Gli scenari migliori funzionano quasi sempre. I tuoi test dovrebbero cercare di forzare errori e casi limite.
    • Hai un test che trasmette flussi difettosi da elaborare?
    • Hai un test che trasmette nomi di file nulli / inesistenti da elaborare?
    • Che cosa accade se i valori numerici da calcolare non sono numerici o se sono analizzati enormi o producono valori enormi quando vengono calcolati?
  • Sorprendentemente i test di scrittura raramente confermano che quello che hai fatto è giusto. Invece ti danno un'idea di come sia utilizzabile, stabile e flessibile il tuo design.

Modifica > Questo è già stato accettato, ma volevo aggiungere qualcosa che ho imparato molto tempo fa tramite Test delle unità pragmatiche :

Test dell'unità con il tuo BICEP destro

  • I risultati sono Giusti ?
  • CORREZIONE B Condizioni estreme
    • C onform al formato previsto
    • O è stato eseguito correttamente
    • R ange è corretto
    • R le eferenze alle dipendenze esterne sono sicure
    • E xistence (null ecc.)
    • C ardinality
    • T iming (le cose accadono nel giusto ordine, con i tempi corretti)
  • I relazioni inverse
  • C ross-Controlla i risultati con altri mezzi
  • I E rors sono forzati
  • Le caratteristiche di P sono entro limiti accettabili
risposta data 30.08.2013 - 01:40
fonte
5

Penso che la tua classe sia difficile da testare perché ha troppe responsabilità. Li menzioni tu stesso

  • Leggi i dati dal file
  • Scrivi dati nel file
  • Esegui calcolo

Idealmente, una classe dovrebbe avere una singola responsabilità o una singola area di responsabilità. In questo caso, dovresti avere sicuramente una classe contenente solo la logica per eseguire i calcoli.

Se lo avessi, forse potresti avere una funzione come questa:

CalculationResult PerformCalculation(Measurement[] measurements)
{ ... }

Questo diventa relativamente facile da testare, dato che puoi facilmente fornire una serie di misure ben definite in un test.

Qui presumo che tu abbia letto dal file una volta per ottenere tutte le misure contemporaneamente. Se raccogli nuove misure scritte nel file nel tempo, la tua classe potrebbe apparire come questa:

class Calculator {
    AddMeasurement(Measurement measurement) { ... }

    CalculationResult PerformCalculation() { ... }
}

Ancora facile da testare, perché è possibile alimentare la classe con una serie di misurazioni ben definite durante il test e leggere il risultato, senza dipendere dal file system.

Una classe diversa potrebbe avere la responsabilità di leggere i dati di misurazione dal file. Questo potrebbe nuovamente essere suddiviso in dati di lettura da un file e analizzare i dati come oggetti di misura, per consentire di testare separatamente la logica di analisi senza dipendenza dal file system, ecc.

Se è difficile scrivere un test unitario, questo è spesso un segno che la tua "unità" ha più responsabilità, troppe dipendenze, o in altri modi non è SOLID .

    
risposta data 03.09.2013 - 22:43
fonte
3

Il test dell'unità dovrebbe testare due serie di casi:

I casi molto semplici: il calcolo è corretto, i totali si sommano, ecc. Usa semplice facile controllare i dati per questi casi.

I casi limite: data di consegna del 29 febbraio 2016, quantità dell'ordine di 999.999, articoli con prezzo $ 0,00, GPS per il polo nord (prova a spostarti ad ovest!) ecc. ecc.

    
risposta data 30.08.2013 - 03:48
fonte
0

Penso che molte delle risposte siano al punto, nel senso che troppi dei test essenziali sembrano come se fossero combinati in un'istanza di test di un'unità. MA.

Molte funzioni richiedono dati complicati e producono risultati complicati. L'elaborazione delle immagini, ad esempio, può avere una funzione compatta che produce una maschera da un'immagine. Un driver di test (direi appropriato) legge un'immagine, la processa, scrive un file immagine e confronta il file risultante con un file immagine di riferimento.

Verificare l'integrazione nell'obiettivo di tutta quella funzionalità sarebbe un test di integrazione. Testare una singola funzione del target, un input complicato per un output complicato, è un test unitario appropriato.

    
risposta data 19.12.2018 - 20:39
fonte

Leggi altre domande sui tag