Python unittest: comment exécuter seulement une partie d'un fichier de test?
-
21-08-2019 - |
Question
J'ai un fichier de test qui contient des tests qui prennent tout à fait beaucoup de temps (ils envoient des calculs à un cluster et attendre le résultat). Tous ces éléments sont en classe TestCase spécifiques.
Comme ils prennent du temps et de plus ne sont pas susceptibles de se briser, je veux être en mesure de choisir si ce sous-ensemble de tests ou ne fonctionne pas (la meilleure façon serait un argument de ligne de commande, à savoir " ./tests.py --offline
» ou quelque chose comme ça), je pouvais courir la plupart des tests souvent et rapidement et l'ensemble de temps en temps, quand j'ai le temps.
Pour l'instant, je viens d'utiliser pour démarrer les unittest.main()
tests.
Merci.
La solution
La valeur par défaut utilise le chargeur unittest.main()
de test par défaut pour faire un GroupTest hors du module dans lequel principale est en cours d'exécution.
Vous ne devez pas utiliser ce comportement par défaut.
Vous pouvez, par exemple, faire trois instances unittest.TestSuite .
-
Le sous-ensemble "rapide".
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
-
Le sous-ensemble "lent".
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
-
Le set "ensemble".
alltests = unittest.TestSuite([fast, slow])
Notez que j'ai ajusté les noms de TestCase pour indiquer rapide par rapport lent. Vous pouvez sous-classe unittest.TestLoader pour analyser les noms des classes et créer plusieurs chargeurs.
Ensuite, votre programme principal peut analyser les arguments de ligne de commande avec optparse ou argparse (disponible depuis 2.7 ou 3.2) pour choisir quelle suite, vous voulez exécuter, rapide, lent ou tout.
Ou bien, vous pouvez avoir confiance que est l'une des sys.argv[1]
trois valeurs et d'utiliser quelque chose d'aussi simple que cela
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Autres conseils
En fait, on peut passer les noms des cas de test sys.argv et seuls les cas seront testés.
Par exemple, supposons que vous avez
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
Vous pouvez appeler
python test.py account
pour des tests uniquement de compte, ou même
$ python test.py account customer
pour que les deux cas testés
Je fais cela à l'aide d'un simple skipIf
:
import os
SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))
@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
def runTest(self):
…
De cette façon je besoin que décore un cas de test déjà existant avec cette ligne unique (pas besoin de créer des suites de test ou similaire, juste que l'on os.getenv()
ligne d'appel au début de mon dossier de test unitaire), et par défaut ce test se sautée.
Si je veux l'exécuter en dépit d'être lent, je viens d'appeler mon script comme ceci:
SLOW_TESTS=1 python -m unittest …
Vous avez deux façons de le faire:
- Définissez votre propre suite de tests pour la classe
- Créer des classes simulées de la connexion du cluster qui renverra les données réelles.
Je suis un fervent partisan de l'approche qu'il seconde; un test unitaire devrait seul test une unité même de code, et non pas des systèmes complexes (comme les bases de données ou clusters). Mais je comprends qu'il ne soit pas toujours possible; parfois, la création de maquettes est tout simplement trop cher, ou le but du test est vraiment dans le système complexe.
Retour à l'option (1), vous pouvez procéder ainsi:
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
et en faisant passer la suite au coureur de test:
unittest.TextTestRunner().run(suite)
Plus d'informations sur la documentation python: http://docs.python.org /library/unittest.html#testsuite-objects
Puisque vous utilisez, vous pouvez simplement unittest.main()
exécuter pour obtenir la python tests.py --help
documentation:
Usage: tests.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
tests.py - run default set of tests
tests.py MyTestSuite - run suite 'MyTestSuite'
tests.py MyTestCase.testSomething - run MyTestCase.testSomething
tests.py MyTestCase - run all 'test*' test methods
in MyTestCase
C'est, vous pouvez simplement faire
python tests.py TestClass.test_method
Vous pouvez également utiliser la fonction unittest.SkipTest()
. Exemple, ajoutez une méthode à votre classe skipOrRunTest
de test comme ceci:
def skipOrRunTest(self,testType):
#testsToRun = 'ALL'
#testsToRun = 'testType1, testType2, testType3, testType4,...etc'
#testsToRun = 'testType1'
#testsToRun = 'testType2'
#testsToRun = 'testType3'
testsToRun = 'testType4'
if ((testsToRun == 'ALL') or (testType in testsToRun)):
return True
else:
print "SKIPPED TEST because:\n\t testSuite '" + testType + "' NOT IN testsToRun['" + testsToRun + "']"
self.skipTest("skipppy!!!")
Ensuite, ajoutez un appel à cette méthode skipOrRunTest au début de chacun de vos tests unitaires comme ceci:
def testType4(self):
self.skipOrRunTest('testType4')
J'ai trouvé une autre solution, basée sur la façon dont le décorateur fonctionne unittest.skip
.
En définissant la et __unittest_skip__
__unittest_skip_why__
.
à base de labels
Je voulais appliquer un système d'étiquetage, d'étiqueter certains tests comme quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, et ainsi de suite.
Ensuite, exécutez all 'quick' tests
ou run everything except 'memoryhog' tests
, la configuration de votre liste blanche / liste noire de base
Mise en œuvre
Je mis en œuvre ce en 2 parties:
- Tout d'abord ajouter des étiquettes à des tests (via une coutume décorateur de classe
@testlabel
) - personnalisé pour identifier les
unittest.TestRunner
tests à sauter, et modifier le contenu de testlist avant d'exécuter.
Mise en oeuvre de travail est dans ce point essentiel: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c
(un exemple entièrement trop longtemps pour mettre ici)
Le résultat étant ...
$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=2)
Tous les tests de classe MyTests1
sont ignorés, car il a l'étiquette foo
.
--whitelist
fonctionne aussi
Regardez en utilisant un TestRunner dédié, comme py.test, nez ou peut-être même zope.testing. Elles ont toutes les options de ligne de commande pour la sélection des tests.
Regardez par exemple que le nez: https://pypi.python.org/pypi /nose/1.3.0
J'ai essayé @ réponse de Slott:
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Mais cela m'a donné l'erreur suivante:
Traceback (most recent call last):
File "functional_tests.py", line 178, in <module>
unittest.TextTestRunner().run(suite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string
Ce qui suit a fonctionné pour moi:
if __name__ == "__main__":
test_class = eval(sys.argv[1])
suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
unittest.TextTestRunner().run(suite)
Je l'ai trouvé une autre façon de sélectionner le test_ * méthodes que je veux seulement courir en ajoutant un attribut à leur disposition. Vous utilisez essentiellement un métaclasse pour décorer les appelables à l'intérieur de la classe TestCase qui ont l'attribut StepDebug avec un décorateur unittest.skip. Plus d'info sur
sauter tous les tests unitaires, mais un en Python en utilisant les décorateurs et les métaclasses
Je ne sais pas si elle est une meilleure solution que celles ci-dessus, je suis juste comme une fournirai option.
n'avez pas trouvé une belle façon de le faire avant, donc le partage ici.
Objectif: Obtenir un ensemble de fichiers de test ensemble afin qu'ils puissent être gérés comme une unité, mais nous pouvons toujours sélectionner l'un d'eux à exécuter par lui-même.
Problème: la méthode discover ne permet pas une sélection facile d'un seul cas de test pour exécuter
.Conception: voir ci-dessous. Cette aplanit l'espace de noms afin peut sélectionner par TestCase nom de la classe, et laisser tomber le préfixe « de tests1.test_core »:
./run-tests TestCore.test_fmap
code
test_module_names = [
'tests1.test_core',
'tests2.test_other',
'tests3.test_foo',
]
loader = unittest.defaultTestLoader
if args:
alltests = unittest.TestSuite()
for a in args:
for m in test_module_names:
try:
alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
except AttributeError as e:
continue
else:
alltests = loader.loadTestsFromNames( test_module_names )
runner = unittest.TextTestRunner( verbosity = opt.verbose )
runner.run( alltests )
Ceci est la seule chose qui a fonctionné pour moi.
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
Quand je l'ai appelé si je devais passer au nom du nom de classe et test. Un peu gênant car je n'ai pas combinaison de classe et le nom de test mémorisé.
python ./tests.py class_Name.test_30311
Suppression du nom de classe et le nom de test exécute tous les tests dans votre dossier. Je trouve cela beaucoup plus facile à traiter alors intégré dans la méthode que je ne change pas vraiment ma commande sur la CLI. Il suffit d'ajouter le paramètre.
Profitez, Keith
J'ai créé un décorateur qui permet de marquer des tests comme des tests lents et de les sauter en utilisant une variable d'environnement
from unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
Maintenant, vous pouvez marquer vos tests aussi lent comme ceci:
@slow_test
def test_my_funky_thing():
perform_test()
Et passer des tests lents en définissant la SKIP_SLOW_TESTS
variable d'environnement:
SKIP_SLOW_TESTS=1 python -m unittest