unittest - Struttura per Automatizzare Test
Scopo: Struttura per Automatizzare Test
Il modulo unittest è basato sulla struttura XUnit progettata da Kent Beck ed Erich Gamma. Lo stesso modello viene ripetuto in molti altri linguaggi, compresi C, Perl, Java e Smalltalk. La struttura implementata da unittest supporta impianti, serie di test e un esecutore di test che consente l'automatizzazione dei test.
Struttura Base di Test
I test, definiti da unittest, hanno due parti: codice per gestire le dipendenze (detti fixtures) e il test stesso. Test individuali sono creati subclassando TestCase
e sovrascrivendo o aggiungendo i metodi appropriati. Nell'esempio seguente, SimplisticTest
ha un singolo metodo test()
, che fallirà se il contenuto di a
è diverso dal contenuto di b
.
# unittest_simple.py
import unittest
class SimplisticTest(unittest.TestCase):
def test(self):
a = 'a'
b = 'a'
self.assertEqual(a, b)
Eseguire Test
Il modo più semplice per eseguire test di unittest è utilizzare il rilevamento automatico tramite l'interfaccia da riga di comando.
$ python3 -m unittest unittest_simple.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
Questo risultato abbreviato comprende il tempo impiegato nell'esecuzione del test, con un indicatore di stato per ciascun test (Il valore "." nella prima riga del risultato significa che un test è stato passato). Per un risultato più dettagliato si includa l'opzione -v
.
$ python3 -m unittest -v unittest_simple.py test (unittest_simple.SimplisticTest) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
Esiti del Test
Ci sono tre possibili esiti, come da tabella di seguito:
ESITO | DESCRIZIONE |
---|---|
ok | Test superato |
FAIL | Test non superato, ed ha sollevato una eccezione AssertionError |
ERROR | Il test ha sollevato una qualunque eccezione diversa da AssertionError |
Non ci sono modi espliciti per forzare il superamento di un test, quindi lo stato di un test dipende dalla presenza o meno di una eccezione.
# unittest_outcomes.py
import unittest
class OutcomesTest(unittest.TestCase):
def testPass(self):
return
def testFail(self):
self.assertFalse(True)
def testError(self):
raise RuntimeError('Errore nel test!')
Quando un test fallisce o genera un errore, il traceback viene incluso nel risultato.
$ python3 -m unittest unittest_outcomes.py EF. ====================================================================== ERROR: testError (unittest_outcomes.OutcomesTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_outcomes.py", line 15, in testError raise RuntimeError('Errore nel test!') RuntimeError: Errore nel test! ====================================================================== FAIL: testFail (unittest_outcomes.OutcomesTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_outcomes.py", line 12, in testFail self.assertFalse(True) AssertionError: True is not false ---------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (failures=1, errors=1)
Nell'esempio precedente, testFail()
fallisce e il traceback mostra la riga di codice che ha provocato il fallimento. Spetta alla persona che legge il risultato del test scoprire il significato del fallimento.
# unittest_failwithmessage.py
import unittest
class FailureMessageTest(unittest.TestCase):
def testFail(self):
self.assertFalse(True, 'Il messaggio di fallimento va qui')
Per facilitare la comprensione della natura di un fallimento di test, i metodi fail*()
ed assert*()
accettano tutti un argomento msg
che può essere usato per produrre un messaggio di errore più dettagliato.
$ python3 -m unittest -v unittest_failwithmessage.py testFail (unittest_failwithmessage.FailureMessageTest) ... FAIL ====================================================================== FAIL: testFail (unittest_failwithmessage.FailureMessageTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_failwithmessage.py", line 8, in testFail self.assertFalse(True, 'Il messaggio di fallimento va qui') AssertionError: True is not false : Il messaggio di fallimento va qui ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1)
Asserire la Verità
La maggior parte dei test asseriscono la veridicità di una qualche condizione. Ci sono due modi diversi per scrivere test che verifichino una condizione di vero, a seconda della prospettiva dell'autore del test e del risultato desiderato dal codice in fase di test.
# unittest_truth.py
import unittest
class TruthTest(unittest.TestCase):
def testAssertTrue(self):
self.assertTrue(True)
def testAssertFalse(self):
self.assertFalse(False)
Se il codice produce un valore che può essere valutato come vero, il metodo assertTrue()
dovrebbe essere utilizzato. Se il codice produce un valore falso, avrebbe più senso utilizzare il metodo assertFalse()
.
$ python3 -m unittest -v unittest_truth.py testAssertFalse (unittest_truth.TruthTest) ... ok testAssertTrue (unittest_truth.TruthTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Verificare Uguaglianza
Come caso particolare, unittest include metodi per verificare l'uguaglianza di due valori.
# unittest_equality.py
import unittest
class EqualityTest(unittest.TestCase):
def testExpectEqual(self):
self.assertEqual(1, 3 - 2)
def testExpectEqualFails(self):
self.assertEqual(2, 3 - 2)
def testExpectNotEqual(self):
self.assertNotEqual(2, 3 - 2)
def testExpectNotEqualFails(self):
self.assertNotEqual(1, 3 - 2)
Quando falliscono, questi metodi particolari producono messaggi di errore che includono il valore utilizzato per il confronto.
$ python3 -m unittest -v unittest_equality.py testExpectEqual (unittest_equality.EqualityTest) ... ok testExpectEqualFails (unittest_equality.EqualityTest) ... FAIL testExpectNotEqual (unittest_equality.EqualityTest) ... ok testExpectNotEqualFails (unittest_equality.EqualityTest) ... FAIL ====================================================================== FAIL: testExpectEqualFails (unittest_equality.EqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality.py", line 12, in testExpectEqualFails self.assertEqual(2, 3 - 2) AssertionError: 2 != 1 ====================================================================== FAIL: testExpectNotEqualFails (unittest_equality.EqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality.py", line 18, in testExpectNotEqualFails self.assertNotEqual(1, 3 - 2) AssertionError: 1 == 1 ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=2)
Quasi Uguali?
Oltre alla corrispondenza stretta, è anche possibile verificare una corrispondenza relativa per valori a virgola mobile usando assertAlmostEqual()
e assertAlmostNotEqual()
.
# unittest_almostequal.py
import unittest
class AlmostEqualTest(unittest.TestCase):
def testEqual(self):
self.assertEqual(1.1, 3.3 - 2.2)
def testAlmostEqual(self):
self.assertAlmostEqual(1.1, 3.3 - 2.2, places=1)
def testNotAlmostEqual(self):
self.assertNotAlmostEqual(1.1, 3.3 - 2.0, places=1)
Gli argomenti sono i valori da confrontare e il numero di cifre decimali da usare per il confronto.
$ python3 -m unittest -v unittest_almostequal.py testAlmostEqual (unittest_almostequal.AlmostEqualTest) ... ok testEqual (unittest_almostequal.AlmostEqualTest) ... FAIL testNotAlmostEqual (unittest_almostequal.AlmostEqualTest) ... ok ====================================================================== FAIL: testEqual (unittest_almostequal.AlmostEqualTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_almostequal.py", line 9, in testEqual self.assertEqual(1.1, 3.3 - 2.2) AssertionError: 1.1 != 1.0999999999999996 ---------------------------------------------------------------------- Ran 3 tests in 0.000s FAILED (failures=1)
Contenitori
Oltre ai generici assertEqual()
e assertNotEqual()
, ci sono metodi speciali per confrontare oggetti contenitore come list
, dict
e set
.
# unittest_equality_container.py
import textwrap
import unittest
class ContainerEqualityTest(unittest.TestCase):
def testCount(self):
self.assertCountEqual(
[1, 2, 3, 2],
[1, 3, 2, 3],
)
def testDict(self):
self.assertDictEqual(
{'a': 1, 'b': 2},
{'a': 1, 'b': 3},
)
def testList(self):
self.assertListEqual(
[1, 2, 3],
[1, 3, 2],
)
def testMultiLineString(self):
self.assertMultiLineEqual(
textwrap.dedent("""
Questa stringa
ha più di una
riga
"""),
textwrap.dedent("""
Questa stringa ha
più di due
righe.
"""),
)
def testSequence(self):
self.assertSequenceEqual(
[1, 2, 3],
[1, 3, 2],
)
def testSet(self):
self.assertSetEqual(
set([1, 2, 3]),
set([1, 3, 2, 4]),
)
def testTuple(self):
self.assertTupleEqual(
(1, 'a'),
(1, 'b'),
)
Ciascun metodo rileva la non uguaglianza utilizzando un formato che ha senso rispetto al tipo in input, rendendo il fallimento del test più facile da comprendere e correggere.
$ python3 -m unittest unittest_equality_container.py FFFFFFF ====================================================================== FAIL: testCount (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 10, in testCount self.assertCountEqual( AssertionError: Element counts were not equal: First has 2, Second has 1: 2 First has 1, Second has 2: 3 ====================================================================== FAIL: testDict (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 16, in testDict self.assertDictEqual( AssertionError: {'a': 1, 'b': 2} != {'a': 1, 'b': 3} - {'a': 1, 'b': 2} ? ^ + {'a': 1, 'b': 3} ? ^ ====================================================================== FAIL: testList (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 22, in testList self.assertListEqual( AssertionError: Lists differ: [1, 2, 3] != [1, 3, 2] First differing element 1: 2 3 - [1, 2, 3] + [1, 3, 2] ====================================================================== FAIL: testMultiLineString (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 28, in testMultiLineString self.assertMultiLineEqual( AssertionError: '\nQuesta stringa\nha più di una\nriga\n' != '\nQuesta stringa ha\npiù di due\nrighe.\n' - Questa stringa + Questa stringa ha ? +++ - ha più di una - riga + più di due + righe. ====================================================================== FAIL: testSequence (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 42, in testSequence self.assertSequenceEqual( AssertionError: Sequences differ: [1, 2, 3] != [1, 3, 2] First differing element 1: 2 3 - [1, 2, 3] + [1, 3, 2] ====================================================================== FAIL: testSet (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 48, in testSet self.assertSetEqual( AssertionError: Items in the second set but not the first: 4 ====================================================================== FAIL: testTuple (unittest_equality_container.ContainerEqualityTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_equality_container.py", line 54, in testTuple self.assertTupleEqual( AssertionError: Tuples differ: (1, 'a') != (1, 'b') First differing element 1: 'a' 'b' - (1, 'a') ? ^ + (1, 'b') ? ^ ---------------------------------------------------------------------- Ran 7 tests in 0.002s FAILED (failures=7)
Si usi assertIn()
per verificare l'appartenenza a un contenitore.
# unittest_in.py
import unittest
class ContainerMembershipTest(unittest.TestCase):
def testDict(self):
self.assertIn(4, {1: 'a', 2: 'b', 3: 'c'})
def testList(self):
self.assertIn(4, [1, 2, 3])
def testSet(self):
self.assertIn(4, set([1, 2, 3]))
Qualsiasi oggetto che supporta l'operatore in
o le API dei contenitori possono essere usati con assertIn()
.
$ python3 -m unittest unittest_in.py FFF ====================================================================== FAIL: testDict (unittest_in.ContainerMembershipTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_in.py", line 9, in testDict self.assertIn(4, {1: 'a', 2: 'b', 3: 'c'}) AssertionError: 4 not found in {1: 'a', 2: 'b', 3: 'c'} ====================================================================== FAIL: testList (unittest_in.ContainerMembershipTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_in.py", line 12, in testList self.assertIn(4, [1, 2, 3]) AssertionError: 4 not found in [1, 2, 3] ====================================================================== FAIL: testSet (unittest_in.ContainerMembershipTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_in.py", line 15, in testSet self.assertIn(4, set([1, 2, 3])) AssertionError: 4 not found in {1, 2, 3} ---------------------------------------------------------------------- Ran 3 tests in 0.000s FAILED (failures=3)
Verificare Eccezioni
Come accennato in precedenza, se un test solleva una eccezione diversa da AssertionError
viene considerato come un errore. Questo è molto utile per scoprire errori mentre si sta modificando codice che ha una copertura di test esistente. Ci sono circostanze nelle quali, comunque, il test dovrebbe verificare che un certo codice produce una eccezione. Ad esempio, se un valore non valido viene dato a un attributo di un oggetto. In questi casi, assertRaises()
rende il codice più pulito rispetto al catturare l'eccezione nel test. Si confrontino questi due test:
# unittest_exception.py
import unittest
def raises_error(*args, **kwds):
raise ValueError('Valore non valido: ' + str(args) + str(kwds))
class ExceptionTest(unittest.TestCase):
def testTrapLocally(self):
try:
raises_error('a', b='c')
except ValueError:
pass
else:
self.fail('Non rilevato ValueError')
def testAssertRaises(self):
self.assertRaises(
ValueError,
raises_error,
'a',
b='c',
)
Il risultato è lo stesso per entrambi, ma il secondo test, che usa assertRaises()
è più conciso.
$ python3 -m unittest -v unittest_exception.py testAssertRaises (unittest_exception.ExceptionTest) ... ok testTrapLocally (unittest_exception.ExceptionTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Impianti di Test
Gli impianti (fixtures) sono risorse esterne necessarie a un test. Ad esempio per verificare una classe potrebbe servire una istanza di un'altra classe che fornisce impostazioni di configurazione o altre risorse condivise. Altri impianti di test potrebbero includere connessioni di database e file temporanei (molte persone potrebbero argomentare che l'utilizzo di risorse esterne rende i test "non unitari", ma sono comunque test e sono comunque utili).
unittest include agganci speciali per configurare e pulire un qualunque impianto serva ai test. Per attivare impianti per ciasun caso di test individuale, si sovrascriva setUp()
su TestCase
. Per pulirli si sovrascriva tearDown()
. Per gestire un insieme di impianti per tutte le istanze di una classe di test si sovrascrivano i metodi della classe setUpClass()
e tearDownClass()
. Per gestire operazioni di impostazione particolarmente esose per tutti i test all'interno di un modulo, si usino le funzioni a livello di modulo setUpModule()
e tearDownModule()
.
# unittest_fixtures.py
import random
import unittest
def setUpModule():
print('In setUpModule()')
def tearDownModule():
print('In tearDownModule()')
class FixturesTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('In setUpClass()')
cls.good_range = range(1, 10)
@classmethod
def tearDownClass(cls):
print('In tearDownClass()')
del cls.good_range
def setUp(self):
super().setUp()
print('\nIn setUp()')
# Scegliere un numero che si conosce essere nell'intervallo.
# L'intervallo è definito com non comprendente il valore finale, quindi
# assicurarsi che non sia incluso nell'insieme dei valori consentiti
# per la scelta.
self.value = random.randint(
self.good_range.start,
self.good_range.stop - 1,
)
def tearDown(self):
print('In tearDown()')
del self.value
super().tearDown()
def test1(self):
print('In test1()')
self.assertIn(self.value, self.good_range)
def test2(self):
print('In test2()')
self.assertIn(self.value, self.good_range)
Quando questo test di esempio viene eseguito, l'ordine di esecuzione dell'impianto e dei metodi di test è evidente.
$ python3 -u -m unittest -v unittest_fixtures.py In setUpModule() In setUpClass() test1 (unittest_fixtures.FixturesTest) ... In setUp() In test1() In tearDown() ok test2 (unittest_fixtures.FixturesTest) ... In setUp() In test2() In tearDown() ok In tearDownClass() In tearDownModule() ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Il metodi tearDown
potrebbero non essere chiamati se si verificano errori nel processo di pulizia degli impianti. Per assicurarsi che un impianto venga sempre rilasciato correttamente si usi addCleanUp()
.
# unittest_addcleanup.py
import random
import shutil
import tempfile
import unittest
def remove_tmpdir(dirname):
print('In remove_tmpdir()')
shutil.rmtree(dirname)
class FixturesTest(unittest.TestCase):
def setUp(self):
super().setUp()
self.tmpdir = tempfile.mkdtemp()
self.addCleanup(remove_tmpdir, self.tmpdir)
def test1(self):
print('\nIn test1()')
def test2(self):
print('\nIn test2()')
Questo esempio di test crea una directory temporanea, quindi usa shutil per la pulizia una volta terminato il test.
$ python3 -u -m unittest -v unittest_addcleanup.py test1 (unittest_addcleanup.FixturesTest) ... In test1() In remove_tmpdir() ok test2 (unittest_addcleanup.FixturesTest) ... In test2() In remove_tmpdir() ok ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
Ripetere Test con Input Differenti
E' spesso utile eseguire la stessa logica di test con input diversi. Invece che definire un metodo di test separato per ciascun piccolo caso, un sistema comunemente adottato è di utilizzare un metodo di test che contiene diverse chiamate di asserzioni. Il problema con questo approccio è che non appena fallisce una asserzione, il resto viene saltato. Una soluzione migliore è l'utilizzo di subTest()
per creare un contesto per un test all'interno del metodo di test. Se il test fallisce, il fallimento viene riportato e le verifiche rimanenti continuano.
# unittest_subtest.py
import unittest
class SubTest(unittest.TestCase):
def test_combined(self):
self.assertRegex('abc', 'a')
self.assertRegex('abc', 'B')
# The next assertions are not verified!
self.assertRegex('abc', 'c')
self.assertRegex('abc', 'd')
def test_with_subtest(self):
for pat in ['a', 'B', 'c', 'd']:
with self.subTest(pattern=pat):
self.assertRegex('abc', pat)
In questo esempio, il metodo test_combined()
non esegue mail le asserzioni per i modelli 'c
' e 'd
'. Il metodo test_with_subtest()
lo fa e riporta correttamente i fallimenti rimanenti. Si noti che l'esecutore del test considera che ci siano solo due casi di test, nonostante siano riportati tre fallimenti.
$ python3 -m unittest -v unittest_subtest.py test_combined (unittest_subtest.SubTest) ... FAIL test_with_subtest (unittest_subtest.SubTest) ... ====================================================================== FAIL: test_combined (unittest_subtest.SubTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_subtest.py", line 10, in test_combined self.assertRegex('abc', 'B') AssertionError: Regex didn't match: 'B' not found in 'abc' ====================================================================== FAIL: test_with_subtest (unittest_subtest.SubTest) (pattern='B') ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_subtest.py", line 18, in test_with_subtest self.assertRegex('abc', pat) AssertionError: Regex didn't match: 'B' not found in 'abc' ====================================================================== FAIL: test_with_subtest (unittest_subtest.SubTest) (pattern='d') ---------------------------------------------------------------------- Traceback (most recent call last): File "/dati/dev/python/pymotw3restyling/dumpscripts/unittest_subtest.py", line 18, in test_with_subtest self.assertRegex('abc', pat) AssertionError: Regex didn't match: 'd' not found in 'abc' ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=3)
Saltare Test
E' spesso utile poter saltare un test se una qualche condizione esterna non viene corrisposta. Ad esempio, quando si scrivono test per verificare il comportamento di una libreria sotto una versione specifica di Python non c'è ragione di eseguire detti test sotto altre versioni di Pyhton. Le classi di test e i metodi possono essere decorati con skip()
per saltare sempre i test. I decoratori skipIf()
e skipUnless()
possono essere utilizzati per verificare una condizione prima di saltare i test corrispondenti.
# unittest_skip.py
import sys
import unittest
class SkippingTest(unittest.TestCase):
@unittest.skip('sempre saltati')
def test(self):
self.assertTrue(False)
@unittest.skipIf(sys.version_info[0] > 2,
'eseguito solo su python 2')
def test_python2_only(self):
self.assertTrue(False)
@unittest.skipUnless(sys.platform == 'Darwin',
'eseguito solo su macOS')
def test_macos_only(self):
self.assertTrue(True)
def test_raise_skiptest(self):
raise unittest.SkipTest('saltato tramite eccezione')
Per condizioni complesse che è difficile esprimere in una singola condizione passata a skip(f()
o skipUnless()
, un caso di test potrebbe sollevare direttamente skipTest
per far sì che il test venga saltato.
$ python3 -m unittest -v unittest_skip.py test (unittest_skip.SkippingTest) ... skipped 'sempre saltati' test_macos_only (unittest_skip.SkippingTest) ... skipped 'eseguito solo su macOS' test_python2_only (unittest_skip.SkippingTest) ... skipped 'eseguito solo su python 2' test_raise_skiptest (unittest_skip.SkippingTest) ... skipped 'saltato tramite eccezione' ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK (skipped=4)
Invece che eliminare test che sono persistentemente rotti, si possono marcare con il decoratore expectedFailure()
in modo che il fallimento venga ignorato.
# unittest_expectedfailure.py
import unittest
class Test(unittest.TestCase):
@unittest.expectedFailure
def test_never_passes(self):
self.assertTrue(False)
@unittest.expectedFailure
def test_always_passes(self):
self.assertTrue(True)
Se un test che ci si attende fallisca viceversa venisse superato, quella condizione viene trattata come uno speciale tipo di fallimento e viene riportato come un unexpected success (successo inatteso - n.d.t.).
$ python3 -m unittest -v unittest_expectedfailure.py test_always_passes (unittest_expectedfailure.Test) ... unexpected success test_never_passes (unittest_expectedfailure.Test) ... expected failure ---------------------------------------------------------------------- Ran 2 tests in 0.000s FAILED (expected failures=1, unexpected successes=1)
Vedere anche:
- unittest
- La documentazione della libreria standard per questo modulo.
- doctest
- Un modo alternativo di eseguire test incorporati in docstring o file esterni di documentazione.
- nose
- Un esecutore di test di terze parti con caratteristiche di scoperta sofisticate.
- pytest
- Un popolare esecutore di test di terze parti con supporto per l'esecuzione distribuita e un sistema di gestione di impianti alternativo.
- testrepository
- Un esecutore di test di terze parti usato dal progetto OpenStack, con supporto per l'esecuzione parallela e la tracciatura dei fallimenti.