Scrivi test di unità professionali in Python

I test sono il fondamento di un solido sviluppo del software. Esistono molti tipi di test, ma il tipo più importante è il test delle unità. I test unitari ti danno la certezza che puoi usare pezzi ben testati come primitivi e affidarti a loro quando li componi per creare il tuo programma. Aumentano il tuo inventario di codice fidato oltre i builtin della tua lingua e la libreria standard. Inoltre, Python fornisce un grande supporto per la scrittura dei test delle unità.

Esecuzione di esempio

Prima di entrare in tutti i principi, euristiche e linee guida, vediamo un test unitario rappresentativo in azione. Il SelfDrivingCar la classe è un'implementazione parziale della logica di guida di un'auto a guida autonoma. Si occupa principalmente del controllo della velocità della macchina. È a conoscenza degli oggetti che ha di fronte, del limite di velocità e della sua destinazione. 

classe SelfDrivingCar (oggetto): def __init __ (self): self.speed = 0 self.destination = Nessuno def _accelerate (self): self.speed + = 1 def _decelerate (self): se self.speed> 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () se distanza < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): pass def _calculate_distance_to_object_in_front(self): pass def _get_speed_limit(self): pass def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop() def __init__(self): self.speed = 0 self.destination = None def _accelerate(self): self.speed += 1 def _decelerate(self): if self.speed > 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () se distance < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): pass def _calculate_distance_to_object_in_front(self): pass def _get_speed_limit(self): pass def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop() 

Ecco un test unitario per il Stop() metodo per stuzzicare l'appetito. Entrerò nei dettagli più tardi. 

dalla classe Unestest di importazione TestCase SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar () def test_stop (self): self.car.speed = 5 self.car.stop () # Verificare che la velocità sia 0 dopo stop self.assertEqual (0, self.car.speed) # Verificare che sia Ok di fermarsi di nuovo se l'auto è già ferma self.car.stop () self.assertEqual (0, self.car.speed)

Linee guida per il test delle unità

Commettere

Scrivere buoni test unitari è un duro lavoro. I test delle unità di scrittura richiedono tempo. Quando apporti modifiche al tuo codice, di solito devi anche cambiare i tuoi test. A volte avrai bug nel tuo codice di test. Ciò significa che devi essere veramente impegnato. I benefici sono enormi, anche per piccoli progetti, ma non sono gratuiti.

Sii disciplinato

Devi essere disciplinato. Sii coerente. Assicurati che i test passino sempre. Non lasciare che i test vengano interrotti perché "sai" che il codice è OK.

Automatizzare

Per aiutarti a essere disciplinato, dovresti automatizzare i tuoi test unitari. I test dovrebbero essere eseguiti automaticamente in punti significativi come pre-commit o pre-distribuzione. Idealmente, il sistema di gestione dei controlli di origine rifiuta il codice che non ha superato tutti i test.

Il codice non testato è rotto per definizione

Se non l'hai testato, non puoi dire che funzioni. Questo significa che dovresti considerarlo rotto. Se è un codice critico, non distribuirlo in produzione.

sfondo

Che cos'è una unità?

Un'unità ai fini del test unitario è un file / modulo contenente un insieme di funzioni correlate o una classe. Se hai un file con più classi, dovresti scrivere un test unitario per ogni classe.

To TDD o Not to TDD

Lo sviluppo basato su test è una pratica in cui si scrivono i test prima di scrivere il codice. Ci sono molti vantaggi a questo approccio, ma ti consiglio di evitarlo se hai la disciplina per scrivere test appropriati più tardi. 

La ragione è che disegno con codice. Scrivo codice, lo guardo, lo riscrivo, lo guardo di nuovo e lo riscrivo molto velocemente. I test di scrittura inizialmente mi limitano e mi rallentano. 

Una volta terminato il progetto iniziale, scriverò immediatamente i test, prima di integrarlo con il resto del sistema. Detto questo, è un ottimo modo per presentarti ai test unitari e garantisce che tutto il tuo codice abbia dei test.

Il modulo Unittest

Il modulo unittest viene fornito con la libreria standard di Python. Fornisce una classe chiamata TestCase, da cui puoi ricavare la tua classe. Quindi puoi ignorare a impostare() metodo per preparare un dispositivo di prova prima di ogni test e / o a classSetUp () metodo di classe per preparare un dispositivo di prova per tutti i test (non ripristinato tra i singoli test). Ci sono corrispondenti demolire() e classTearDown () metodi che puoi sovrascrivere.

Ecco le parti rilevanti dal nostro SelfDrivingCarTest classe. Io uso solo il impostare() metodo. Creo un nuovo SelfDrivingCar istanza e memorizzarlo self.car quindi è disponibile per ogni test.

dall'unità di test di importazione non valida SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar ()

Il passo successivo è scrivere metodi di test specifici per testare il codice sotto test SelfDrivingCar classe in questo caso - sta facendo quello che dovrebbe fare. La struttura di un metodo di prova è piuttosto standard:

  • Preparare l'ambiente (facoltativo).
  • Preparare il risultato previsto.
  • Chiama il codice sotto test.
  • Asserire che il risultato effettivo corrisponde al risultato atteso.

Si noti che il risultato non deve essere l'output di un metodo. Può essere un cambio di stato di una classe, un effetto collaterale come aggiungere una nuova riga in un database, scrivere un file o inviare un'e-mail.

Ad esempio, il Stop() metodo del SelfDrivingCar la classe non restituisce nulla, ma cambia lo stato interno impostando la velocità su 0. Il assertEqual () metodo fornito dal TestCase la classe base è usata qui per verificare quella chiamata Stop() ha funzionato come previsto.

def test_stop (self): self.car.speed = 5 self.car.stop () # Verificare che la velocità sia 0 dopo l'arresto di self.assertEqual (0, self.car.speed) # Verificare che sia Ok di fermarsi di nuovo se il auto è già ferma self.car.stop () self.assertEqual (0, self.car.speed)

Ci sono in realtà due test qui. Il primo test è assicurarsi che se la velocità dell'auto è 5 e Stop() viene chiamato, quindi la velocità diventa 0. Quindi, un altro test è per garantire che nulla vada storto se si chiama Stop() di nuovo quando la macchina è già ferma.

Più tardi, introdurrò molti altri test per funzionalità aggiuntive.

Il modulo Doctest

Il modulo doctest è piuttosto interessante. Ti consente di utilizzare esempi di codice interattivo nella docstring e di verificare i risultati, incluse le eccezioni sollevate. 

Non uso o raccomando doctest per sistemi su larga scala. Il corretto test delle unità richiede molto lavoro. Il codice di prova è in genere molto più grande del codice in prova. Le docstring non sono il mezzo giusto per scrivere test completi. Sono fantastici, però. Ecco cosa a fattoriale la funzione con doc test è simile a:

import math def factorial (n): "" "Restituisce il fattoriale di n, un intero esatto> = 0. Se il risultato è abbastanza piccolo da rientrare in un int, restituisci un int. Else restituisce un lungo. >>> [fattoriale (n) per n nell'intervallo (6)] [1, 1, 2, 6, 24, 120] >>> [fattoriale (lungo (n)) per n nell'intervallo (6)] [1, 1, 2, 6, 24, 120] >>> fattoriale (30) 265252859812191058636308480000000L >>> fattoriale (30L) 265252859812191058636308480000000L >>> fattoriale (-1) Traceback (ultima chiamata ultima): ... ValoreErrore: n deve essere> = 0 Fattori dei galleggianti sono OK, ma il float deve essere un numero intero esatto: >>> fattoriale (30.1) Traceback (ultima chiamata ultima): ... ValoreErrore: n deve essere intero esatto >>> fattoriale (30.0) 265252859812191058636308480000000L Non deve inoltre essere ridicolmente grande : >>> factorial (1e100) Traceback (ultima chiamata ultima): ... OverflowError: n troppo grande "" "se non n> = 0: genera ValueError (" n deve essere> = 0 ") se math.floor (n )! = n: genera ValueError ("n deve essere intero esatto") se n + 1 == n: # cattura un valore come 1e300 raise OverflowE rror ("n troppo grande") risultato = 1 fattore = 2 mentre fattore <= n: result *= factor factor += 1 return result if __name__ == "__main__": import doctest doctest.testmod()

Come puoi vedere, la docstring è molto più grande del codice della funzione. Non promuove la leggibilità.

Esecuzione di test

OK. Hai scritto i tuoi test unitari. Per un sistema di grandi dimensioni, avrai decine / centinaia / migliaia di moduli e classi su possibilmente più directory. Come esegui tutti questi test?

Il modulo unittest offre varie funzionalità per raggruppare i test e eseguirli a livello di programmazione. Scopri Caricamento ed esecuzione dei test. Ma il modo più semplice è la scoperta dei test. Questa opzione è stata aggiunta solo in Python 2.7. Pre-2.7 è possibile utilizzare il naso per scoprire ed eseguire test. Nose ha alcuni altri vantaggi come eseguire le funzioni di test senza dover creare una classe per i casi di test. Ma per lo scopo di questo articolo, continuiamo con l'unittest.

Per scoprire ed eseguire i test basati su unittest, è sufficiente digitare sulla riga di comando:

python -m unittest scopri

Unittest analizzerà tutti i file e le sottodirectory, eseguirà tutti i test che trova e fornirà un bel report oltre al runtime. Se vuoi vedere quali test è in esecuzione, puoi aggiungere il flag -v:

python -m unittest discover -v

Esistono diversi flag che controllano l'operazione:

python -m unittest -h Uso: python -m unittest [opzioni] [test] Opzioni: -h, --help Mostra questo messaggio -v, --verbose Output dettagliato -q, --quiet Output minimo -f, - failfast Stop al primo errore -c, --catch Catch control-C e risultati del display -b, - buffer Buffer stdout e stderr durante le esecuzioni di test Esempi: python -m unittest test_module - Esegui test da test_module python -m unittest module.TestClass - Esegui test da module.TestClass python -m unittest module.Class.test_method - Esegui il metodo di test specificato [test] può essere un elenco di qualsiasi numero di moduli di test, classi e metodi di test. Uso alternativo: python -m unittest discover [opzioni] Opzioni: -v, --verbose Output dettagliato -f, --failfast Interrompi al primo errore -c, --catch Catch control-C e visualizza risultati -b, --buffer Buffer stdout e stderr durante test runs -s directory Directory per avviare discovery ('.' Default) -p pattern Pattern per abbinare i file di test ('test * .py' default) -t directory Directory di livello superiore del progetto (default per avviare la directory ) Per il rilevamento dei test, tutti i moduli di test devono essere importabili dalla directory di livello superiore del progetto.

Copertura di prova

La copertura del test è un campo spesso trascurato. Copertura significa quanto del tuo codice è effettivamente testato dai tuoi test. Ad esempio, se hai una funzione con un se altro dichiarazione e si prova solo il Se ramo, quindi non si sa se il altro filiale funziona o no. Nel seguente esempio di codice, la funzione Inserisci() controlla il tipo dei suoi argomenti. Se entrambi sono numeri interi, li aggiunge semplicemente. 

Se entrambe sono stringhe, tenta di convertirle in numeri interi e di aggiungerle. Altrimenti solleva un'eccezione. Il test_add () la funzione verifica il Inserisci() funzione con argomenti che sono sia interi sia con argomenti che sono float e verifica il comportamento corretto in ciascun caso. Ma la copertura del test è incompleta. Il caso degli argomenti stringa non è stato testato. Di conseguenza, il test ha esito positivo, ma l'errore di battitura nel ramo in cui gli argomenti sono entrambe le stringhe non è stato rilevato (si veda 'intg' lì?).

import unittest def add (a, b): "" "Questa funzione aggiunge due numeri a, b e restituisce la loro somma a e b può numeri interi" "" se isinstance (a, int) e isinstance (b, int): restituisce un + b elseif isinstance (a, str) e isinstance (B, str): int di ritorno (a) + INTG (b) altro: sollevare eccezione ( 'argomenti validi') class test (unittest.TestCase): def test_add (auto) : self.assertEqual (5, add (2, 3)) self.assertEqual (15, add (-6, 21)) self.assertRaises (Exception, add, 4.0, 5.0) unittest.main () 

Ecco l'output:

---------------------------------------------------------------------- Ran 1 test in 0.000s OK Processo terminato con codice di uscita 0

Test delle unità hands-on

Scrivere test unitari di forza industriale non è facile o semplice. Ci sono molte cose da considerare e dei compromessi da fare.

Design per Testability

Se il tuo codice è quello che viene chiamato formalmente codice spaghetti o una grande palla di fango dove diversi livelli di astrazione sono mescolati tra loro e ogni pezzo di codice dipende da ogni altro pezzo di codice, avrai difficoltà a provarlo. Inoltre, ogni volta che cambi qualcosa, dovrai aggiornare anche una serie di test.

La buona notizia è che il design del software per uso generico è esattamente quello che ti serve per la testabilità. In particolare, il codice modulare ben strutturato, in cui ogni componente ha una chiara responsabilità e interagisce con altri componenti tramite interfacce ben definite, renderà la scrittura dei buoni test unitari un piacere.

Ad esempio, il nostro SelfDrivingCar la classe è responsabile per il funzionamento ad alto livello della macchina: andare, fermarsi, navigare. Ha un calculate_distance_to_object_in_front () metodo che non è ancora stato implementato. Questa funzionalità dovrebbe probabilmente essere implementata da un sottosistema totalmente separato. Può includere la lettura di dati da vari sensori, l'interazione con altre auto che si guidano da soli, un intero stack di visione artificiale per analizzare le immagini di più telecamere.

Vediamo come funziona in pratica. Il SelfDrivingCar accetterà un argomento chiamato object_detector che ha un metodo chiamato calculate_distance_to_object_in_front (), e delegherà questa funzionalità a questo oggetto. Ora, non è necessario testare l'unità perché il object_detector è responsabile (e dovrebbe essere testato) per questo. Vuoi ancora testare unitamente il fatto che stai usando il object_detector propriamente.

Classe SelfDrivingCar (oggetto): def __init __ (self, object_detector): self.object_detector self.speed = 0 self.destination = Nessuno def _calculate_distance_to_object_in_front (self): ritorno self.object_detector.calculate_distance_to_object_in_front ()

Costi-benefici

La quantità di sforzi che mettete in testing dovrebbe essere correlata al costo del fallimento, alla stabilità del codice e alla facilità con cui è possibile correggere se vengono rilevati problemi lungo la linea.

Ad esempio, la nostra classe di auto a guida autonoma è super critica. Se la Stop() il metodo non funziona correttamente, la nostra auto a guida automatica potrebbe uccidere persone, distruggere proprietà e far deragliare l'intero mercato delle auto a guida autonoma. Se sviluppi un'auto a guida autonoma, sospetto che la tua unità collauda per Stop() il metodo sarà un po 'più rigoroso del mio. 

D'altra parte, se un singolo pulsante nella tua applicazione web su una pagina che è sepolto tre livelli sotto il tuo sfarfallio della pagina principale un po 'quando qualcuno fa clic, puoi correggerlo, ma probabilmente non aggiungerà un test di unità dedicato per questo caso. L'economia non lo giustifica. 

Mindset test

La mentalità del test è importante. Un principio che uso è che ogni pezzo di codice ha almeno due utenti: l'altro codice che lo sta usando e il test che lo sta testando. Questa semplice regola aiuta molto nel design e nelle dipendenze. Se ricordi che devi scrivere un test per il tuo codice, non aggiungerai molte dipendenze difficili da ricostruire durante i test.

Ad esempio, supponiamo che il tuo codice debba calcolare qualcosa. Per fare ciò, è necessario caricare alcuni dati da un database, leggere un file di configurazione e consultare dinamicamente alcune API REST per informazioni aggiornate. Tutto questo può essere richiesto per vari motivi, ma mettere tutto questo in una singola funzione renderà piuttosto difficile il test unitario. È ancora possibile con il mocking, ma è molto meglio strutturare correttamente il tuo codice.

Pure funzioni

Il codice più semplice da testare è puramente funzioni. Le funzioni pure sono funzioni che accedono solo ai valori dei loro parametri, non hanno effetti collaterali e restituiscono lo stesso risultato ogni volta che vengono chiamati con gli stessi argomenti. Non cambiano lo stato del tuo programma, non accedono al file system o alla rete. I loro benefici sono troppi per contare qui. 

Perché sono facili da testare? Perché non è necessario impostare un ambiente speciale da testare. Basta passare argomenti e testare il risultato. Sai anche che finché il codice in prova non cambia, il tuo test non deve cambiare. 

Confrontalo con una funzione che legge un file di configurazione XML. Il test dovrà creare un file XML e passare il suo nome file al codice sotto test. Nessun grosso problema. Ma supponiamo che qualcuno abbia deciso che XML è abominevole e che tutti i file di configurazione devono essere in JSON. Svolgono la loro attività e convertono tutti i file di configurazione in JSON. Eseguono tutti i test compresi i test e loro tutti passaggio! 

Perché? Perché il codice non è cambiato. Si aspetta comunque un file di configurazione XML e il test costruisce ancora un file XML per esso. Ma in produzione, il tuo codice otterrà un file JSON, che non riuscirà ad analizzare.

Test di gestione degli errori

La gestione degli errori è un'altra cosa fondamentale da testare. Fa anche parte del design. Chi è responsabile della correttezza dell'input? Ogni funzione e metodo dovrebbe essere chiaro a riguardo. Se è responsabilità della funzione, dovrebbe verificare il suo input, ma se è la responsabilità del chiamante, la funzione può solo andare in giro per la sua attività e assumere che l'input sia corretto. La correttezza complessiva del sistema sarà garantita dall'esecuzione di test per il chiamante per verificare che trasmetta solo l'input corretto alla funzione.

In genere, si desidera verificare l'input sull'interfaccia pubblica per il codice in quanto non si sa necessariamente chi chiamerà il proprio codice. Diamo un'occhiata a guidare() metodo dell'auto che guida. Questo metodo si aspetta un parametro 'destinazione'. Il parametro 'destination' verrà utilizzato in seguito nella navigazione, ma il metodo drive non fa nulla per verificare che sia corretto. 

Supponiamo che la destinazione sia una tupla di latitudine e longitudine. Ci sono tutti i tipi di test che possono essere fatti per verificare che sia valido (ad esempio la destinazione nel mezzo del mare). Per i nostri scopi, assicuriamoci che si tratti di una tupla di galleggianti nell'intervallo da 0.0 a 90.0 per la latitudine e da -180.0 a 180.0 per la longitudine.

Ecco l'aggiornato SelfDrivingCar classe. Ho implementato banalmente alcuni dei metodi non implementati perché il guidare() metodo chiama alcuni di questi metodi direttamente o indirettamente.

Classe SelfDrivingCar (oggetto): def __init __ (self, object_detector): self.object_detector = object_detector self.speed = 0 self.destination = Nessuno def _accelerate (self): self.speed + = 1 def _decelerate (self): se self. velocità> 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () se distance < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): return True def _calculate_distance_to_object_in_front(self): return self.object_detector.calculate_distance_to_object_in_front() def _get_speed_limit(self): return 65 def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop()

Per verificare la gestione degli errori nel test, inoltrerò argomenti non validi e verificherò che siano correttamente rifiutati. Puoi farlo usando il self.assertRaises () metodo di unittest.TestCase. Questo metodo ha esito positivo se il codice in esame aumenta effettivamente un'eccezione.

Vediamolo in azione. Il test di guida() metodo passa latitudine e longitudine al di fuori dell'intervallo valido e si aspetta che il guidare() metodo per sollevare un'eccezione.

dall'unità di importazione untest TestCase da self_driving_car import AutoDrivingCar class MockObjectDetector (oggetto): def calculate_distance_to_object_in_front (self): return 20 class SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar (MockObjectDetector ()) def test_stop (self): self.car.speed = 5 self.car.stop () # Verificare che la velocità sia 0 dopo l'arresto di self.assertEqual (0, self.car.speed) # Verificare che sia Ok di fermarsi di nuovo se l'auto è già ferma autonomamente. car.stop () self.assertEqual (0, self.car.speed) def test_drive (self): # valido destinazione self.car.drive ((55.0, 66.0)) # destinazione non valido sbagliate gamma self.assertRaises (Exception, auto .car.drive, (-55.0, 200.0))

Il test fallisce, perché il guidare() il metodo non controlla i suoi argomenti per la validità e non genera un'eccezione. Ottieni una bella relazione con informazioni complete su cosa non è riuscito, dove e perché.

python -m unittest scoprire test_drive -v (untitled.test_self_driving_car.SelfDrivingCarTest) ... FAIL test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok ======================= =============================================== FAIL: test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ------------------------------------------- --------------------------- Traceback (ultima chiamata più recente): File "/Users/gigi/PycharmProjects/untitled/test_self_driving_car.py" , riga 29, in test_drive self.assertRaises (Exception, self.car.drive, (-55.0, 200.0)) AssertionError: Exception not raised -------------------- -------------------------------------------------- Esegui 2 test in 0.000 FAILED (guasti = 1)

Per risolverlo aggiorniamo il guidare() metodo per verificare effettivamente l'intervallo dei suoi argomenti:

def drive (auto, destinazione): lat, lon = destinazione se non (0.0 <= lat <= 90.0): raise Exception('Latitude out of range') if not (-180.0 <= lon <= 180.0): raise Exception('Latitude out of range') self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop()

Ora passano tutti i test.

python -m unittest scoprire test_drive -v (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok ----------------------- ----------------------------------------------- Esegui 2 test in 0.000 OK 

Test dei metodi privati

Dovresti testare ogni funzione e metodo? In particolare, dovresti testare metodi privati ​​chiamati solo dal tuo codice? La risposta generalmente insoddisfacente è: "Dipende". 

Cercherò di essere utile qui e dirti da cosa dipende. Sai esattamente chi chiama il tuo metodo privato: è il tuo codice. Se i tuoi test per i metodi pubblici che chiamano il tuo metodo privato sono completi, allora testerai esaurientemente i tuoi metodi privati. Ma se un metodo privato è molto complicato, puoi testarlo in modo indipendente. Usa il tuo giudizio.

Come organizzare i test delle unità

In un sistema di grandi dimensioni, non è sempre chiaro come organizzare i test. Dovresti avere un unico file con tutti i test per un pacchetto o un file di test per ogni classe? I test devono essere nello stesso file del codice sotto test o nella stessa directory?

Ecco il sistema che uso. I test dovrebbero essere completamente separati dal codice sotto test (quindi non uso doctest). Idealmente, il tuo codice dovrebbe essere in un pacchetto. I test per ogni pacchetto devono essere presenti in una directory di pari livello del pacchetto. Nella directory dei test, dovrebbe esserci un file per ogni modulo del pacchetto chiamato test_

Ad esempio, se nel pacchetto sono presenti tre moduli: module_1.py, module_2.py e module_3.py, dovresti avere tre file di test: test_module_1.py, test_module_2.py e test_module_3.py sotto la directory dei test. 

Questa convenzione ha diversi vantaggi. Rende chiaro solo sfogliando le directory che non hai dimenticato di testare completamente alcuni moduli. Aiuta anche a organizzare i test in blocchi di dimensioni ragionevoli. Supponendo che i tuoi moduli siano ragionevolmente dimensionati, il codice di test per ogni modulo sarà nel suo stesso file, che potrebbe essere un po 'più grande del modulo sotto test, ma comunque qualcosa che si adatta comodamente in un file. 

Conclusione

I test unitari sono la base del codice solido. In questo tutorial, ho esplorato alcuni principi e linee guida per i test unitari e spiegato il ragionamento dietro alcune best practice. Più grande è il sistema che stai costruendo, più diventano importanti i test unitari. Ma i test unitari non sono abbastanza. Altri tipi di test sono necessari anche per i sistemi su larga scala: test di integrazione, test delle prestazioni, test di carico, test di penetrazione, test di accettazione, ecc..