17

Estoy extendiendo el marco de python 2.7 unittest para hacer algunas pruebas de funcionamiento. Una de las cosas que me gustaría hacer es evitar que todas las pruebas se ejecuten dentro de una prueba, y dentro de un método setUpClass(). A veces, si falla una prueba, el programa está tan roto que ya no sirve para seguir probando, por lo que quiero evitar que las pruebas se ejecuten.¿Cómo detener todas las pruebas desde dentro de una prueba o setUp usando unittest?

Me di cuenta de que un TestResult tiene un atributo shouldStop y un método stop(), pero no estoy seguro de cómo obtener acceso a eso dentro de una prueba.

¿Alguien tiene alguna idea? ¿Hay una mejor manera?

+0

Utilizo esto para abortar la ejecución de prueba antes de ejecutar cualquier prueba si la aplicación bajo prueba está configurada con ajustes de prod. (en realidad, con cualquier configuración que no sea prueba). –

Respuesta

4

Aquí hay otra respuesta que se me ocurrió después de un tiempo:

En primer lugar, he añadido una nueva excepción:

class StopTests(Exception): 
""" 
Raise this exception in a test to stop the test run. 

""" 
    pass 

Luego añade un nuevo assert a mi clase de prueba de niño:

def assertStopTestsIfFalse(self, statement, reason=''): 
    try: 
     assert statement    
    except AssertionError: 
     result.addFailure(self, sys.exc_info()) 

y último anulé la función run para incluir este derecho debajo de la llamada testMethod():

except StopTests: 
    result.addFailure(self, sys.exc_info()) 
    result.stop() 

Me gusta más, ya que cualquier prueba ahora tiene la capacidad de detener todas las pruebas, y no hay código específico de cpython.

+0

¿Dónde se generan los StopTests? –

3

Actualmente, solo puede detener las pruebas a nivel de suite. Una vez que está en un TestCase, el método stop() para el TestResult no se utiliza al iterar a través de las pruebas.

Un poco relacionado con su pregunta, si usa Python 2.7, puede usar el indicador -f/--failfast al llamar a su prueba con python -m unittest. Esto detendrá la prueba en la primera falla.

Ver 25.3.2.1. failfast, catch and buffer command line options

También puede considerar el uso de Nose a ejecutar las pruebas y el uso de la -x, --stopflag para detener la prueba temprana.

+0

Gracias. Vi la opción failfast, pero no siempre quiero fallar en el primer error, solo en lugares seleccionados. Creo que debería editar mi pregunta para mencionar que estoy usando Python 2.7. – user197674

0

Miré la clase TestCase y decidí crear una subclase. La clase simplemente anula run(). He copiado el método y a partir de la línea 318 en la clase original añadido este:

# this is CPython specific. Jython and IronPython may do this differently 
if testMethod.func_code.co_argcount == 2: 
    testMethod(result) 
else: 
    testMethod() 

Tiene algún código específico CPython allí para saber si el método de ensayo puede aceptar otro parámetro, pero ya que estoy usando todas partes CPython , este no es un problema para mí

11

En caso de estar interesado, aquí es un ejemplo sencillo de cómo se podría tomar una decisión sí mismo acerca de salir de un banco de pruebas limpiamente con py.test:

# content of test_module.py 
import pytest 
counter = 0 
def setup_function(func): 
    global counter 
    counter += 1 
    if counter >=3: 
     pytest.exit("decided to stop the test run") 

def test_one(): 
    pass 
def test_two(): 
    pass 
def test_three(): 
    pass 

y si se ejecuta este que se obtiene:

$ pytest test_module.py 
============== test session starts ================= 
platform linux2 -- Python 2.6.5 -- pytest-1.4.0a1 
test path 1: test_module.py 

test_module.py .. 

!!!! Exit: decided to stop the test run !!!!!!!!!!!! 
============= 2 passed in 0.08 seconds ============= 

También puede poner la llamada py.test.exit() dentro de una prueba o en un complemento específico del proyecto.

Sidenote: py.test admite de forma nativa py.test --maxfail=NUM para implementar la detención después de NUM fallas.

Sidenote2: py.test tiene soporte limitado para ejecutar pruebas en el estilo tradicional unittest.TestCase.

+0

Gracias. No estaba al tanto de ese marco de prueba. Lo echaré un vistazo. – user197674

+2

Versión actual de Py.Test: import pytest y luego puedes hacer pytest.exit ("tu mensaje") – RvdK

+1

Esto también funciona bien dentro de un dispositivo pytest declarado en un archivo conftest.py, con scope = 'session', autouse = Cierto. Esto se ejecutará antes de cada prueba en el proyecto. Llamar a pytest.exit desde aquí puede abortar toda la ejecución de prueba antes de ejecutar cualquier prueba. Utilizo esto para abortar las pruebas si se están ejecutando usando prod config (en realidad, cualquier configuración que no sea de prueba) –

1

En el bucle de prueba de unittest.TestSuite, no es una condición break al comienzo:

class TestSuite(BaseTestSuite): 

    def run(self, result, debug=False): 
     topLevel = False 
     if getattr(result, '_testRunEntered', False) is False: 
      result._testRunEntered = topLevel = True 

     for test in self: 
      if result.shouldStop: 
       break 

Así que estoy usando un conjunto de pruebas a medida como esta:

class CustomTestSuite(unittest.TestSuite): 
    """ This variant registers the test result object with all ScriptedTests, 
     so that a failed Loign test can abort the test suite by setting result.shouldStop 
     to True 
    """ 
    def run(self, result, debug=False): 
     for test in self._tests: 
      test.result = result 

     return super(CustomTestSuite, self).run(result, debug) 

con un resultado de la prueba personalizada clase como esta:

class CustomTestResult(TextTestResult): 
    def __init__(self, stream, descriptions, verbosity): 
     super(CustomTestResult, self).__init__(stream, descriptions, verbosity) 
     self.verbosity = verbosity 
     self.shouldStop = False 

y mis clases de prueba son como:

class ScriptedTest(unittest.TestCase): 
    def __init__(self, environment, test_cfg, module, test): 
     super(ScriptedTest, self).__init__() 
     self.result = None 

En determinadas condiciones, a continuación, aborto el conjunto de pruebas; por ejemplo, el conjunto de pruebas comienza con un inicio de sesión, y si eso no funciona, lo que no tiene que probar el resto:

try: 
     test_case.execute_script(test_command_list) 
    except AssertionError as e: 
     if test_case.module == 'session' and test_case.test == 'Login': 
      test_case.result.shouldStop = True 
      raise TestFatal('Login failed, aborting test.') 
     else: 
      raise sys.exc_info() 

Luego uso el conjunto de pruebas de la siguiente manera:

suite = CustomTestSuite() 

    self.add_tests(suite) 

    result = unittest.TextTestRunner(verbosity=self.environment.verbosity, stream=UnitTestLoggerStream(self.logger), 
            resultclass=CustomTestResult).run(suite) 

No estoy seguro de si hay una mejor manera de hacerlo, pero se comporta correctamente para mis pruebas.

0

Aunque no obtendrá los informes de prueba habituales de las pruebas realizadas hasta el momento, una forma muy fácil de detener la ejecución de prueba desde un método TestCase es simplemente elevar KeyboardInterrupt dentro del método.

Se puede ver cómo única KeyboardInterrupt se le permite propagarse por el interior corredor de prueba unittest 's de mirar el código de here CPython dentro testPartExecutor().

Cuestiones relacionadas