Exécution d'un test unique depuis unittest.TestCase via la ligne de commande

257

Dans notre équipe, nous définissons la plupart des cas de test comme ceci:

Une classe "framework" ourtcfw.py:

import unittest

class OurTcFw(unittest.TestCase):
    def setUp:
        # something

    # other stuff that we want to use everywhere

et beaucoup de cas de test comme testMyCase.py:

import localweather

class MyCase(OurTcFw):

    def testItIsSunny(self):
        self.assertTrue(localweather.sunny)

    def testItIsHot(self):
        self.assertTrue(localweather.temperature > 20)

if __name__ == "__main__":
    unittest.main()

Lorsque j'écris un nouveau code de test et que je veux l'exécuter souvent et gagner du temps, ce que je fais, c'est que je mets "__" devant tous les autres tests. Mais c'est lourd, me distrait du code que j'écris et le bruit de validation que cela crée est tout simplement ennuyeux.

Par exemple, lorsque j'apporte des modifications à testItIsHot(), je veux pouvoir faire ceci:

$ python testMyCase.py testItIsHot

et ont unittestcouru seulement testItIsHot()

Comment puis-je y parvenir?

J'ai essayé de réécrire la if __name__ == "__main__":partie, mais depuis que je suis nouveau sur Python, je me sens perdu et continue de me baser sur tout le reste que les méthodes.

Alois Mahdal
la source

Réponses:

311

Cela fonctionne comme vous le suggérez - il vous suffit de spécifier également le nom de la classe:

python testMyCase.py MyCase.testItIsHot
phihag
la source
2
Oh mon! Étant donné que les tests doivent être exécutés sur python2.6 (99% du temps, je peux tester les tests eux-mêmes avec python2.7), je regardais la doc 2.6.8 et j'ai tellement manqué! :-)
Alois Mahdal
1
Je viens de remarquer que cela ne fonctionne que si la méthode est appelée "test *", donc malheureusement, elle ne peut pas être utilisée pour exécuter occasionnellement un test qui est "désactivé" en
renommant
4
Ne fonctionne pas pour les tests dans un sous-répertoire - le cas le plus courant dans un programme Python mature.
Tom Swirly
4
@ TomSwirly Je ne peux pas vérifier maintenant mais je pense que vous pouvez le faire en créant (vide) à l' __init__.pyintérieur de ce répertoire (et sous-répertoires, le cas échéant) et en appelant par exemple. python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal
1
Rien ne se passe quand je fais ça. J'ai trouvé des solutions de contournement, mais j'espérais que cette méthode fonctionnerait pour moi.
Joe Flack
153

Si vous organisez vos cas de test, c'est-à-dire, suivez la même organisation comme le code réel et utilisez également les importations relatives pour les modules dans le même package

Vous pouvez également utiliser le format de commande suivant:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Documentation Python3 pour cela: https://docs.python.org/3/library/unittest.html#command-line-interface

Ajay M
la source
C'est tellement clunk Java-esque. "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ... mieux espérons que vous ne vous êtes pas modularisé en suites comme une personne sensée qui teste son code.
Joshua Detwiler
69

Cela peut bien fonctionner comme vous le pensez

python testMyCase.py MyCase.testItIsHot

Et il existe une autre façon de simplement tester testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)
Yarkee
la source
11
J'ai trouvé la deuxième partie de cette réponse extrêmement utile: j'écris des tests dans Eclipse + PyDev et je ne veux pas passer en ligne de commande!
Giovanni Di Milia
25

Si vous consultez l'aide du module unittest, il vous indique plusieurs combinaisons qui vous permettent d'exécuter des classes de cas de test à partir d'un module et des méthodes de test à partir d'une classe de cas de test.

python3 -m unittest -h

[...]

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method

Il ne vous oblige pas à définir a unittest.main()comme comportement par défaut de votre module.

skqr
la source
2
+1 et puisque la terminologie peut être déroutante si elle est nouvelle dans une langue (et usageest même étrangement incohérente): l'exécution python -m unittest module_test.TestClass.test_methodsuppose un fichier module_test.py(exécuté à partir du répertoire courant; et __init.py__n'est pas requis); et module_test.pycontient class TestClass(unittest.TestCase)...ce qui contient def test_method(self,...)(cela fonctionne aussi pour moi sur python 2.7.13)
michael
11

Peut-être que ce sera utile pour quelqu'un. Si vous souhaitez exécuter uniquement des tests d'une classe spécifique:

if __name__ == "__main__":
    unittest.main(MyCase())

Cela fonctionne pour moi en python 3.6

Bohdan
la source
3

Inspiré par @yarkee, je l'ai combiné avec une partie du code que j'ai déjà reçu. Vous pouvez également l'appeler à partir d'un autre script, simplement en appelant la fonction run_unit_tests()sans avoir besoin d'utiliser la ligne de commande, ou tout simplement l'appeler à partir de la ligne de commande avec python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Malheureusement, cela ne fonctionne que pour Python 3.3ou supérieur:

import unittest

class LineBalancingUnitTests(unittest.TestCase):

    @classmethod
    def setUp(self):
        self.maxDiff = None

    def test_it_is_sunny(self):
        self.assertTrue("a" == "a")

    def test_it_is_hot(self):
        self.assertTrue("a" != "b")

Code du coureur:

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from .somewhere import LineBalancingUnitTests

def create_suite(classes, unit_tests_to_run):
    suite = unittest.TestSuite()
    unit_tests_to_run_count = len( unit_tests_to_run )

    for _class in classes:
        _object = _class()
        for function_name in dir( _object ):
            if function_name.lower().startswith( "test" ):
                if unit_tests_to_run_count > 0 \
                        and function_name not in unit_tests_to_run:
                    continue
                suite.addTest( _class( function_name ) )
    return suite

def run_unit_tests():
    runner = unittest.TextTestRunner()
    classes =  [
        LineBalancingUnitTests,
    ]

    # Comment all the tests names on this list, to run all Unit Tests
    unit_tests_to_run =  [
        "test_it_is_sunny",
        # "test_it_is_hot",
    ]
    runner.run( create_suite( classes, unit_tests_to_run ) )

if __name__ == "__main__":
    print( "\n\n" )
    run_unit_tests()

En modifiant un peu le code, vous pouvez passer un tableau avec tous les tests unitaires que vous souhaitez appeler:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

    runner.run( suite( classes, unit_tests_to_run ) )
...

Et un autre fichier:

import my_test_file

# Comment all the tests names on this list, to run all Unit Tests
unit_tests_to_run = \
[
    "test_it_is_sunny",
    # "test_it_is_hot",
]

my_test_file.run_unit_tests( unit_tests_to_run )

Alternativement, vous pouvez utiliser https://docs.python.org/3/library/unittest.html#load-tests-protocol et définir la méthode suivante sur votre module / fichier de test:

def load_tests(loader, standard_tests, pattern):
    suite = unittest.TestSuite()

    # To add a single test from this file
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )

    # To add a single test class from this file
    suite.addTests( unittest.TestLoader().loadTestsFromTestCase( LineBalancingUnitTests ) )

    return suite

Si vous souhaitez limiter l'exécution à un seul fichier de test, il vous suffit de définir le modèle de découverte de test sur le seul fichier dans lequel vous avez défini la load_tests()fonction.

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest

test_pattern = 'mytest/module/name.py'
PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )

loader = unittest.TestLoader()
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

suite = loader.discover( start_dir, test_pattern )
runner = unittest.TextTestRunner( verbosity=2 )
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )

sys.exit( not results.wasSuccessful() )

Références:

  1. Problème avec sys.argv [1] lorsque le module unittest est dans un script
  2. Existe-t-il un moyen de parcourir et d'exécuter toutes les fonctions d'une classe Python?
  3. bouclage sur toutes les variables membres d'une classe en python

Alternativement au dernier exemple de programme principal, j'ai trouvé la variation suivante après avoir lu l' unittest.main()implémentation de la méthode:

  1. https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L65
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import unittest

PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

from testing_package import main_unit_tests_module
testNames = ["TestCaseClassName.test_nameHelloWorld"]

loader = unittest.TestLoader()
suite = loader.loadTestsFromNames( testNames, main_unit_tests_module )

runner = unittest.TextTestRunner(verbosity=2)
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )
utilisateur
la source
3

TL; DR : Cela fonctionnerait très probablement:

python mypkg/tests/test_module.py MyCase.testItIsHot

L'explication :

  • La manière pratique

    python mypkg/tests/test_module.py MyCase.testItIsHot

    fonctionnerait MAIS son hypothèse tacite est que vous avez déjà cet extrait de code conventionnel à l'intérieur (généralement à la fin de) votre fichier de test.

    if __name__ == "__main__":
        unittest.main()
  • La manière gênante

    python -m unittest mypkg.tests.test_module.TestClass.test_method

    fonctionnerait toujours, sans vous obliger à avoir cet if __name__ == "__main__": unittest.main()extrait de code dans votre fichier source de test.

Alors pourquoi la 2e méthode est-elle considérée comme peu pratique? Parce que ce serait pénible (_ insérez ici une partie de votre corps _) de taper ce long chemin délimité par des points à la main. En première méthode, la mypkg/tests/test_module.pypièce peut être complétée automatiquement, soit par un shell moderne, soit par votre éditeur.

PS: Si vous pensiez que cette partie du corps se situe quelque part sous votre taille, vous êtes une personne authentique. :-) Je veux dire "articulation du doigt". Trop de frappe serait mauvais pour vos articulations. ;-)

RayLuo
la source