Risposte:
Il modo in cui sono organizzati i test è piuttosto irrilevante rispetto all'importanza di averli.
La cosa più importante per una buona suite di test è che copre tutte le funzionalità; ciò assicura che ogni volta che viene introdotto un difetto di regressione, noterai subito. Se scrivere un test per ciascun metodo, un test per ogni combinazione di valori di input di un metodo o anche un test per ogni possibile percorso del codice all'interno di quel metodo è meno importante. Se organizzare questi test in poche o molte classi di test è ancora meno importante: una suite di test dovrebbe sempre avere successo per intero, quindi non importa se fallisce uno dei tre o due dei diciassette test - entrambi sono sbagliati e devono essere fisso.
Naturalmente, anche il codice di test è un codice, quindi dovrebbe seguire le normali migliori pratiche per essere gestibile, modulare, ecc. Ma questo deve essere deciso da quanto sia ben gestibile la suite di test stessa, non da come le classi di test si relazionano al classi che testano. Entrambe le politiche che menzioni possono aiutarti a ricordare dove trovare le cose se le segui costantemente, ma per questo la coerenza è più importante della scelta della politica.
Alla tua domanda specifica, la convenzione JUnit, deve avere una corrispondenza 1: 1 tra le classi di applicazione ( Foo1.java
, Foo2.java
) e le classi di test JUnit ( Foo1Test.java
, Foo2Test.java
).
Detto questo, concordo pienamente con l'evidenziazione di Kilian sull'importanza dello spirito / obiettivi del test unitario rispetto a qualsiasi schema organizzativo che potrebbe essere utilizzato. Scegli alcune convenzioni e mantienile con loro per la maggior parte del tempo, mentre consenti di consentire eccezioni alle tue convenzioni, quando sono garantite.
È meglio avere una classe separata per ciascun metodo o avere solo una classe di prova per ogni classe effettiva?
Se hai la necessità di scrivere classi di test separate per i metodi di una classe, hai una progettazione sbagliata. I metodi dovrebbero essere piccoli, facili da leggere, facili da testare e facili da cambiare. I test potrebbero essere un po 'più lunghi del codice originale a causa della creazione di dati di test, derisione ecc., Ma non dovrebbero essere significativamente più lunghi. Se lo sono, la tua classe sottoposta a test è troppo complessa e fa sicuramente più di una cosa ( principio di responsabilità singola ): lavorare sul tuo progetto.
Penso che sia "una classe di test per metodo" che "una classe di test per classe" sono in genere troppo estremi.
In generale, si desidera disporre di un controllo per metodo di test / unit test. Ad esempio, potrebbero essere asserzioni multiple per verificare che a list.isEmpty = true
e list.Length = 0
, quindi, un metodo di test / unit test per comportamento.
Ciò semplifica la creazione di un nome del metodo di prova che descrive il comportamento. Vuoi raggruppare i metodi di test in una classe di test, quindi quando leggi il test classname.test method
, ha senso. Di solito, quelli hanno un codice di installazione condiviso che è quindi facile da inserire nella configurazione / dispositivo di prova. A seconda della classe sotto test, questa può essere una classe di test per l'intera classe e può anche essere una classe di test per un metodo. Ma di solito, sarà da qualche parte nel mezzo.
Come per il codice normale, si desidera avere i test il più leggibili possibile. Ciò che aiuta per me è seguire lo stile BDD dato-quando-allora o organizzare-agire-asserire di organizzare il codice del test. Una classe di test potrebbe dare questo, è impostata. Quindi, ogni metodo di test in quella classe usa quel dato (o parte di esso) e ne ha uno quando e uno poi.
Pensa anche ai test unitari come documentazione su come utilizzare la funzionalità della classe sotto test. Con buoni test unitari, puoi leggere i test per scoprire come utilizzare la funzionalità della classe che desideri utilizzare e quali saranno gli effetti esattamente.
Quando qualcosa si interrompe e un test unitario fallisce, vuoi che sia il più semplice possibile capire cosa si è rotto, un'affermazione per test aiuta molto qui. Quando ci sono più asserzioni in un test, solo il primo fallisce e poi il metodo termina, quindi non sai se anche l'altro comportamento testato nei test seguenti viene interrotto fino a quando non risolvi la cosa che ha fallito il primo asserzione. Con una sola affermazione, tutti gli altri metodi di test vengono comunque eseguiti ed è molto più veloce capire la profondità del fallimento.
Certo, sono d'accordo con Kilian Foth: in pratica, puoi essere fortunato ad avere alcuni test unitari per il codice su cui stai lavorando. E qualsiasi piccolo test localizzato è meglio di nessun test, o solo i grandi test di integrazione che vengono eseguiti sul server di build richiedono molto tempo e di solito non sono molto localizzati (non ti dicono velocemente dove si trova l'errore - tu ci dovrò lavorare un po ').
Penso che sia giusto che ogni classe abbia la sua classe di test. L'idea è di centralizzare tutti i test unitari relativi alla classe target in una singola classe test. Quindi, ad esempio, MyClass.java
verrà chiamata la sua classe di test MyClassTest.java
.