J'ai un package avec un répertoire "tests" dans lequel je stocke mes tests unitaires. Mon colis ressemble à:
.
├── LICENSE
├── models
│ └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│ ├── db
│ │ └── test_employee.py
│ └── test_tc.py
└── todo.txt
À partir de mon répertoire de packages, je souhaite pouvoir trouver les deux tests/test_tc.py
et tests/db/test_employee.py
. Je préférerais ne pas avoir à installer une bibliothèque tierce (nose
ou etc) ou avoir à construire manuellement un TestSuite
pour l'exécuter.
Il y a sûrement un moyen de dire unittest discover
ne pas arrêter de chercher une fois qu'il a trouvé un test? python -m unittest discover -s tests
trouvera tests/test_tc.py
et python -m unittest discover -s tests/db
trouvera tests/db/test_employee.py
. N'y a-t-il pas un moyen de trouver les deux?
En creusant un peu, il semble que tant que les modules plus profonds restent importables, ils seront découverts via python -m unittest discover
. La solution était alors simplement d'ajouter un __init__.py
fichier dans chaque répertoire pour en faire des packages.
.
├── LICENSE
├── models
│ └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│ ├── db
│ │ ├── __init__.py # NEW
│ │ └── test_employee.py
│ ├── __init__.py # NEW
│ └── test_tc.py
└── todo.txt
Tant que chaque répertoire a un __init__.py
, python -m unittest discover
peut importer les test_*
module.
Si vous êtes d'accord avec l'ajout d'un fichier __init__.py
Dans les tests, vous pouvez y mettre une fonction load_tests
Qui gérera la découverte pour vous.
Si un nom de package de test (répertoire avec
__init__.py
) Correspond au modèle, le package sera vérifié pour une fonction 'load_tests'. Si cela existe, il sera appelé avec loader, tests, pattern.Si load_tests existe alors la découverte ne pas récursive dans le package, load_tests est responsable du chargement de tous les tests dans le package.
Je suis loin d'être convaincu que c'est la meilleure façon, mais une façon d'écrire cette fonction serait:
import os
import pkgutil
import inspect
import unittest
# Add *all* subdirectories to this module's path
__path__ = [x[0] for x in os.walk(os.path.dirname(__file__))]
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for memname, memobj in inspect.getmembers(mod):
if inspect.isclass(memobj):
if issubclass(memobj, unittest.TestCase):
print("Found TestCase: {}".format(memobj))
for test in loader.loadTestsFromTestCase(memobj):
print(" Found Test: {}".format(test))
suite.addTest(test)
print("=" * 70)
return suite
Assez moche, je suis d'accord.
Tout d'abord, vous ajoutez tous les sous-répertoires au chemin du package de test ( Docs ).
Ensuite, vous utilisez pkgutil
pour parcourir le chemin, à la recherche de packages ou de modules.
Quand il en trouve un, il vérifie ensuite les membres du module pour voir si ce sont des classes, et si ce sont des classes, si ce sont des sous-classes de unittest.TestCase
. S'ils le sont, les tests à l'intérieur des classes sont chargés dans la suite de tests.
Alors maintenant, depuis l'intérieur de la racine de votre projet, vous pouvez taper
python -m unittest discover -p tests
Utilisation du commutateur de modèle -p
. Si tout se passe bien, vous verrez ce que j'ai vu, qui ressemble à ceci:
Found TestCase: <class 'test_tc.TestCase'>
Found Test: testBar (test_tc.TestCase)
Found Test: testFoo (test_tc.TestCase)
Found TestCase: <class 'test_employee.TestCase'>
Found Test: testBar (test_employee.TestCase)
Found Test: testFoo (test_employee.TestCase)
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
C'est ce qui était attendu, chacun de mes deux fichiers d'exemple contenait deux tests, testFoo
et testBar
chacun.
Edit: Après quelques recherches supplémentaires, il semble que vous puissiez spécifier cette fonction comme:
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for test in loader.loadTestsFromModule(mod):
print("Found Tests: {}".format(test._tests))
suite.addTests(test)
Cela utilise la méthode loader.loadTestsFromModule()
au lieu de la méthode loader.loadTestsFromTestCase()
que j'ai utilisée ci-dessus. Il modifie toujours le chemin du package tests
et le parcourt à la recherche de modules, ce qui, je pense, est la clé ici.
La sortie semble un peu différente maintenant, car nous ajoutons une suite de tests trouvée à la fois à notre principale suite de tests suite
:
python -m unittest discover -p tests
Found Tests: [<test_tc.TestCase testMethod=testBar>, <test_tc.TestCase testMethod=testFoo>]
Found Tests: [<test_employee.TestCase testMethod=testBar>, <test_employee.TestCase testMethod=testFoo>]
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
Mais nous obtenons toujours les 4 tests que nous attendions, dans les deux classes, dans les deux sous-répertoires.