web-dev-qa-db-fra.com

Python unittest: comment n'exécuter qu'une partie d'un fichier de test?

J'ai un fichier de test qui contient des tests prenant beaucoup de temps (ils envoient des calculs à un cluster et attendent le résultat). Tous ces éléments appartiennent à la classe TestCase spécifique.

Comme ils prennent du temps et qu’ils ne risquent en outre pas de casser, je voudrais pouvoir choisir si ce sous-ensemble de tests est exécuté ou non (le meilleur moyen serait d’utiliser un argument en ligne de commande, par exemple "./tests.py --offline" ou quelque chose comme ça), donc je pouvais faire la plupart des tests souvent et rapidement et l’ensemble du jeu de temps en temps, quand j’avais le temps.

Pour l'instant, j'utilise simplement unittest.main() pour lancer les tests.

Merci.

66
Gohu

La unittest.main() par défaut utilise le chargeur de test par défaut pour créer un TestSuite à partir du module dans lequel main est en cours d'exécution.

Vous n'êtes pas obligé d'utiliser ce comportement par défaut.

Vous pouvez, par exemple, créer trois unittest.TestSuite instances.

  1. Le sous-ensemble "rapide".

    fast = TestSuite()
    fast.addTests( TestFastThis )
    fast.addTests( TestFastThat )
    
  2. Le sous-ensemble "lent".

    slow = TestSuite()
    slow.addTests( TestSlowAnother )
    slow.addTests( TestSlowSomeMore )
    
  3. L'ensemble "entier".

    alltests = unittest.TestSuite([fast, slow])
    

Notez que j'ai ajusté les noms de TestCase pour indiquer Fast vs. Slow. Vous pouvez sous-classer Unittest.TestLoader pour analyser les noms de 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 la suite que vous souhaitez exécuter, rapide, lente ou complète.

Ou, vous pouvez avoir confiance que sys.argv[1] est l’une des trois valeurs et utiliser quelque chose d'aussi simple que cela

if __== "__main__":
    suite = eval(sys.argv[1])  # Be careful with this line!
    unittest.TextTestRunner().run(suite)
48
S.Lott

Pour exécuter un seul test spécifique, vous pouvez utiliser:

$ python -m unittest test_module.TestClass.test_method

Plus d'informations ici

76
Amit Kotlovski

En fait, on peut transmettre les noms du scénario de test sous le nom sys.argv et seuls ces scénarios seront testés.

Par exemple, supposons que vous ayez

class TestAccount(unittest.TestCase):
    ...

class TestCustomer(unittest.TestCase):
    ...

class TestShipping(unittest.TestCase):
    ...

account = TestAccount
customer = TestCustomer
shipping = TestShipping

Tu peux appeler 

python test.py account

d'avoir seulement des tests de compte, ou même

$ python test.py account customer

d'avoir les deux cas testés

10
Tzury Bar Yochay

Vous avez essentiellement deux façons de le faire:

  1. Définissez votre propre suite de tests pour la classe
  2. Créez des classes fictives de la connexion au cluster qui renverront les données réelles.

Je suis un ardent défenseur de la deuxième approche. un test unitaire devrait tester uniquement une unité de code, et non des systèmes complexes (tels que des bases de données ou des clusters). Mais je comprends que ce n’est pas toujours possible; Parfois, créer des maquettes coûte tout simplement trop cher, ou le but du test est vraiment de rester dans le système complexe.

De retour à l'option (1), vous pouvez procéder de la manière suivante:

suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))

puis en passant la suite au testeur:

unittest.TextTestRunner().run(suite)

Plus d'informations sur la documentation Python: http://docs.python.org/library/unittest.html#testsuite-objects

8
rob

Je fais cela en utilisant une 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 n'ai besoin que de décorer un scénario de test existant avec cette seule ligne (inutile de créer des suites de tests ou similaires, mais simplement cette ligne d'appel os.getenv() au début de mon fichier de test d'unité) et, par défaut, ce test est ignoré.

Si je veux l'exécuter malgré ma lenteur, j'appelle simplement mon script comme suit:

SLOW_TESTS=1 python -m unittest …
8
Alfe

Puisque vous utilisez unittest.main(), vous pouvez simplement exécuter python tests.py --help pour obtenir la 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
5
Thomas Ahle

J'ai trouvé une autre solution, basée sur le fonctionnement du décorateur unittest.skip . En définissant __unittest_skip__ et __unittest_skip_why__.

Basé sur les étiquettes

Je voulais appliquer un système d'étiquetage, étiqueter des tests comme suit: quick, slow, glacier, memoryhog, cpuhog, core, etc.

Ensuite, lancez all 'quick' tests ou run everything except 'memoryhog' tests, votre configuration de base de liste blanche/liste noire

La mise en oeuvre

J'ai implémenté ceci en 2 parties:

  1. Ajoutez d’abord des étiquettes aux tests (via un décorateur de classe @testlabel personnalisé)
  2. Personnaliser unittest.TestRunner pour identifier les tests à ignorer et modifier le contenu de la liste de tests avant exécution.

L'implémentation de travail est dans cet Gist: https://Gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c

(un exemple pleinement fonctionnel était trop long à 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 la classe MyTests1 sont ignorés car ils portent l'étiquette foo.

--whitelist fonctionne aussi

2
FraggaMuffin

Ou vous pouvez utiliser la fonction unittest.SkipTest(). Exemple, ajoutez une méthode skipOrRunTest à votre classe 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!!!")

Ajoutez ensuite un appel à cette méthode skipOrRunTest au tout début de chacun de vos tests unitaires, comme suit:

def testType4(self):
    self.skipOrRunTest('testType4')
2
joe

J'ai essayé la réponse de @ slott:

if __== "__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 __== "__main__":
    test_class = eval(sys.argv[1])
    suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
    unittest.TextTestRunner().run(suite)
1
Dirty Penguin

Envisagez d'utiliser un testrunner dédié, tel que py.test, nose ou éventuellement zope.testing. Ils ont tous des options de ligne de commande pour la sélection des tests.

Recherchez par exemple Nose: https://pypi.python.org/pypi/nose/1.3.0

1
Lennart Regebro

J'ai créé un décorateur qui permet de marquer les tests comme des tests lents et de les ignorer à l'aide d'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 lentement que ceci:

@slow_test
def test_my_funky_thing():
    perform_test()

Et ignorez les tests lents en définissant la variable d’environnement SKIP_SLOW_TESTS:

SKIP_SLOW_TESTS=1 python -m unittest
0
devsnd

N'ayant pas trouvé de moyen agréable de le faire auparavant, partagez ici.

Objectif: réunir un ensemble de fichiers de test afin de pouvoir les exécuter en tant qu’unité, Mais nous pouvons toujours sélectionner l’un d’eux à exécuter par lui-même. 

Problème: la méthode de découverte ne permet pas de sélectionner facilement un seul cas de test à exécuter.

Design: voir ci-dessous. Ceci aplatit l’espace de noms peut donc être sélectionné par nom de classe TestCase et ne pas utiliser le préfixe "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 )
0
FDS

C'est la seule chose qui a fonctionné pour moi.

if __== '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))

Lorsque je l’ai appelé, j’ai dû donner le nom de la classe et le nom du test. Un peu gênant puisque je n'ai pas de combinaison de nom de classe et de test mémorisée.

python ./tests.py nom_classe.test_30311

La suppression du nom de la classe et du nom du test exécute tous les tests de votre fichier. Je trouve BEAUCOUP plus facile à gérer que la méthode intégrée car je ne change pas vraiment ma commande sur la CLI. Il suffit d'ajouter le paramètre.

Profitez, Keith

0
Keith

J'ai trouvé un autre moyen de sélectionner les méthodes test_ * que je souhaite exécuter uniquement en leur attribuant un attribut. Vous utilisez essentiellement une métaclasse pour décorer les appelables de la classe TestCase ayant l'attribut StepDebug avec un décorateur unittest.skip. Plus d'infos sur

Ignorer tous les tests unitaires sauf un en Python en utilisant des décorateurs et des métaclasses

Je ne sais pas si c'est une meilleure solution que celles ci-dessus, je la propose simplement comme option.

0
Satrapes