web-dev-qa-db-fra.com

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

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 __== "__main__":
    unittest.main()

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

Donc, par exemple en apportant des modifications à testItIsHot(), je veux pouvoir le faire:

$ python testMyCase.py testItIsHot

et avoir unittest run seulementtestItIsHot()

Comment puis-je y arriver?

J'ai essayé de réécrire la partie if __== "__main__":, mais comme je ne connais pas encore Python, je me sens perdu et je continue à fouiller dans tout ce qui est autre que les méthodes.

216
Alois Mahdal

Cela fonctionne comme vous le suggérez - vous devez simplement spécifier le nom de la classe:

python testMyCase.py MyCase.testItIsHot
253
phihag

Si vous organisez vos cas de test, suivez la même organisation que le code réel et utilisez également les importations relatives pour les modules du 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

128
Ajay M

Cela peut bien fonctionner comme vous le devinez

python testMyCase.py MyCase.testItIsHot

Et il existe un autre moyen de simplement tester testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)
56
Yarkee

Si vous extrayez de 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 n'est pas nécessaire que vous définissiez une unittest.main() comme comportement par défaut de votre module.

20
skqr

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

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

Cela fonctionne pour moi dans python 3.6

6
Bohdan

Inspiré par @ yarkee je l'ai combiné avec une partie du code que j'ai déjà obtenu. Vous pouvez également appeler cela à partir d'un autre script, simplement en appelant la fonction run_unit_tests() sans avoir besoin d'utiliser la ligne de commande, ou simplement en l'appelant à 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.3 ou 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 __== "__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 )

Vous pouvez également 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()
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )
    return suite

Si vous souhaitez limiter l'exécution à un seul 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 fonction load_tests().

#! /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] quand le module unittest est dans un script
  2. Y a-t-il un moyen de parcourir et d'exécuter toutes les fonctions d'une classe Python?
  3. en boucle sur toutes les variables membres d'une classe en python

En guise d'alternative au dernier exemple de programme principal, j'ai proposé la variante suivante après avoir lu l'implémentation de la méthode unittest.main():

  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 )

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

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )
1
user