Hai ragione, i tuoi test non dovrebbero verificare che il random
modulo stia facendo il suo lavoro; unittest dovrebbe solo testare la classe stessa, non il modo in cui interagisce con altri codici (che dovrebbero essere testati separatamente).
Naturalmente è del tutto possibile che il codice usi in modo random.randint()
errato; o invece stai chiamando random.randrange(1, self._sides)
e il tuo dado non lancia mai il valore più alto, ma sarebbe un diverso tipo di bug, non uno che potresti catturare con unittest. In tal caso, l' die
unità funziona come previsto, ma il design stesso era difettoso.
In questo caso, userei il derisione per sostituire la randint()
funzione e verificherei solo che è stata chiamata correttamente. Python 3.3 e versioni successive vengono forniti con il unittest.mock
modulo per gestire questo tipo di test, ma è possibile installare il mock
pacchetto esterno su versioni precedenti per ottenere la stessa identica funzionalità
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
Con derisione, il tuo test ora è molto semplice; ci sono solo 2 casi, davvero. Il caso predefinito per un dado a 6 facce e il caso di lati personalizzati.
Esistono altri modi per sostituire temporaneamente la randint()
funzione nello spazio dei nomi globale di Die
, ma il mock
modulo lo rende più semplice. Il @mock.patch
decoratore qui si applica a tutti i metodi di prova nel caso di test; ad ogni metodo di test viene passato un argomento in più, la random.randint()
funzione derisa , in modo che possiamo testare contro la simulazione per vedere se è stata effettivamente chiamata correttamente. L' return_value
argomento specifica cosa viene restituito dalla simulazione quando viene chiamato, quindi possiamo verificare che il die.roll()
metodo abbia effettivamente restituito il risultato "casuale" a noi.
Ho usato un'altra best practice unittesting di Python qui: importare la classe sotto test come parte del test. Il _make_one
metodo esegue l'importazione e la creazione di istanze all'interno di un test , in modo che il modulo di test continui a caricarsi anche se si è commesso un errore di sintassi o un altro errore che impedirà l'importazione del modulo originale.
In questo modo, se si commette un errore nel codice del modulo stesso, i test verranno comunque eseguiti; falliranno, raccontandoti l'errore nel tuo codice.
Per essere chiari, i test di cui sopra sono semplicistici all'estremo. L'obiettivo qui non è quello di verificare che random.randint()
è stato chiamato con gli argomenti giusti, per esempio. Invece, l'obiettivo è verificare che l'unità stia producendo i risultati giusti dati determinati input, in cui tali input includono i risultati di altre unità non sotto test. Deridendo il random.randint()
metodo si ottiene il controllo di un altro input al codice.
Nei test del mondo reale , il codice effettivo dell'unità sotto test sarà più complesso; la relazione con gli input passati all'API e il modo in cui vengono successivamente invocate altre unità può essere ancora interessante e il derisione ti darà accesso a risultati intermedi e ti consentirà di impostare i valori di ritorno per quelle chiamate.
Ad esempio, nel codice che autentica gli utenti rispetto a un servizio OAuth2 di terze parti (un'interazione a più fasi), si desidera verificare che il codice trasmetta i dati corretti a quel servizio di terze parti e che consenta di deridere diverse risposte di errore che Il servizio di terze parti ritornerebbe, permettendoti di simulare diversi scenari senza dover costruire tu stesso un server OAuth2 completo. Qui è importante testare che le informazioni di una prima risposta sono state gestite correttamente e sono state passate a una chiamata del secondo stadio, quindi si desidera vedere che il servizio deriso viene chiamato correttamente.