Ejecución de una sola prueba desde unittest.TestCase a través de la línea de comandos

257

En nuestro equipo, definimos la mayoría de los casos de prueba como este:

Una clase "marco" ourtcfw.py:

import unittest

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

    # other stuff that we want to use everywhere

y muchos casos de prueba como 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()

Cuando escribo un nuevo código de prueba y quiero ejecutarlo con frecuencia, y ahorrar tiempo, lo que hago es poner "__" delante de todas las demás pruebas. Pero es engorroso, me distrae del código que estoy escribiendo y el ruido de confirmación que crea es simplemente molesto.

Entonces, por ejemplo, al hacer cambios en testItIsHot(), quiero poder hacer esto:

$ python testMyCase.py testItIsHot

y solo he unittestcorrido testItIsHot()

¿Cómo puedo lograr eso?

Traté de reescribir la if __name__ == "__main__":parte, pero como soy nuevo en Python, me siento perdido y sigo criticando todo lo que no sean los métodos.

Alois Mahdal
fuente

Respuestas:

311

Esto funciona como sugiere, solo tiene que especificar el nombre de la clase también:

python testMyCase.py MyCase.testItIsHot
phihag
fuente
2
¡Oh mi! Dado que las pruebas se ejecutarán en python2.6 (el 99% del tiempo puedo probar las pruebas con python2.7), ¡estaba viendo 2.6.8 doc y me perdí mucho! :-)
Alois Mahdal
1
Acabo de notar que esto funciona solo si el método se llama "prueba *", por lo que desafortunadamente no puede usarse para ejecutar ocasionalmente pruebas que están "deshabilitadas" por cambio de nombre
Alois Mahdal
44
No funciona para pruebas en un subdirectorio, el caso más común en un programa Python maduro.
Tom Swirly
44
@TomSwirly No puedo comprobarlo ahora, pero creo que puedes hacerlo creando (vacío) __init__.pydentro de ese directorio (y subdirectorios, si los hay) y llamando, por ejemplo. python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal
1
No pasa nada cuando hago esto. Encontré soluciones, pero esperaba que este método funcionara para mí.
Joe Flack
153

Si organiza sus casos de prueba, es decir, siga la misma organización como el código real y también use importaciones relativas para módulos en el mismo paquete

También puede usar el siguiente formato de comando:

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

Documentación de Python3 para esto: https://docs.python.org/3/library/unittest.html#command-line-interface

Ajay M
fuente
Esto es tan torpemente Java-esque. "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ... mejor espero que no se haya modularizado en suites como una persona cuerda que prueba su código.
Joshua Detwiler
69

Puede funcionar bien como adivinas

python testMyCase.py MyCase.testItIsHot

Y hay otra forma de probar testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)
Yarkee
fuente
11
La segunda parte de esta respuesta me pareció extremadamente útil: ¡estoy escribiendo pruebas en Eclipse + PyDev y no quiero cambiar a la línea de comando!
Giovanni Di Milia
25

Si consulta la ayuda del módulo unittest, le informa sobre varias combinaciones que le permiten ejecutar clases de caso de prueba desde un módulo y métodos de prueba desde una clase de caso de prueba.

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

No requiere que defina a unittest.main()como el comportamiento predeterminado de su módulo.

skqr
fuente
2
+1 y dado que la terminología puede ser confusa si es nueva en un idioma (y usagees extrañamente inconsistente): la ejecución python -m unittest module_test.TestClass.test_methodasume un archivo module_test.py(se ejecuta desde el directorio actual; y no__init.py__ es obligatorio); y contiene cuál contiene (esto también funciona para mí en python 2.7.13)module_test.pyclass TestClass(unittest.TestCase)...def test_method(self,...)
michael
11

Tal vez, será útil para alguien. En caso de que desee ejecutar solo pruebas de una clase específica:

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

A mí me funciona en Python 3.6

Bohdan
fuente
3

Inspirado por @yarkee, lo combiné con parte del código que ya obtuve. También puede llamarlo desde otro script, simplemente llamando a la función run_unit_tests()sin necesidad de usar la línea de comando, o simplemente llamándolo desde la línea de comando con python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Lamentablemente, esto solo funciona para Python 3.3o superior:

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")

Código de corredor:

#! /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()

Editando un poco el código, puede pasar una matriz con todas las pruebas unitarias que le gustaría llamar:

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

    classes = \
    [
        LineBalancingUnitTests,
    ]

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

Y otro archivo:

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 )

Alternativamente, puede usar https://docs.python.org/3/library/unittest.html#load-tests-protocol y definir el siguiente método en su módulo / archivo de prueba:

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 desea limitar la ejecución a un solo archivo de prueba, solo necesita establecer el patrón de descubrimiento de prueba en el único archivo donde definió la load_tests()función.

#! /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() )

Referencias

  1. Problema con sys.argv [1] cuando el módulo unittest está en un script
  2. ¿Hay alguna manera de recorrer y ejecutar todas las funciones en una clase de Python?
  3. recorrer todas las variables miembro de una clase en python

Alternativamente al último ejemplo del programa principal, se me ocurrió la siguiente variación después de leer la unittest.main()implementación del método:

  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() )
usuario
fuente
3

TL; DR : Esto muy probablemente funcionaría:

python mypkg/tests/test_module.py MyCase.testItIsHot

La explicación :

  • La manera conveniente

    python mypkg/tests/test_module.py MyCase.testItIsHot

    funcionaría PERO su suposición tácita es que ya tiene este fragmento de código convencional dentro (generalmente al final de) su archivo de prueba.

    if __name__ == "__main__":
        unittest.main()
  • La manera inconveniente

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

    siempre funcionaría, sin requerir que tengas ese if __name__ == "__main__": unittest.main()fragmento de código en tu archivo fuente de prueba.

Entonces, ¿por qué el segundo método se considera inconveniente? Porque sería un dolor en el (_ inserte una de las partes de su cuerpo aquí _) escribir a mano ese largo camino delimitado por puntos. Mientras que en el primer método, la mypkg/tests/test_module.pyparte se puede completar automáticamente, ya sea por un shell moderno o por su editor.

PD: Si pensabas que esa parte del cuerpo está debajo de tu cintura, eres una persona auténtica. :-) Quiero decir "articulación del dedo". Escribir demasiado sería malo para sus articulaciones. ;-)

RayLuo
fuente