¿Cómo configuro y desarmo correctamente mi clase Pytest con pruebas?

100

Estoy usando selenio para las pruebas de extremo a extremo y no puedo entender cómo usar setup_classy teardown_classmétodos.

Necesito configurar el navegador en el setup_classmétodo, luego realizar un montón de pruebas definidas como métodos de clase y finalmente salir del navegador en el teardown_classmétodo.

Pero lógicamente parece una mala solución, porque de hecho mis pruebas no funcionarán con class, sino con object. Paso el selfparámetro dentro de cada método de prueba, para poder acceder a las variables de los objetos:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

E incluso parece no ser correcto crear una instancia de navegador para una clase. Debería crearse para cada objeto por separado, ¿verdad?

Entonces, necesito usar los métodos __init__y en __del__lugar de setup_classy teardown_class?

avasin
fuente

Respuestas:

93

De acuerdo con la finalización del dispositivo / ejecución del código de desmontaje , la mejor práctica actual para la configuración y el desmontaje es usar en yieldlugar de return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Ejecutarlo da como resultado

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Otra forma de escribir código de desmontaje es aceptando un requestobjeto -context en su función de dispositivo y llamando a su request.addfinalizermétodo con una función que realiza el desmontaje una o varias veces:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))
Everett Toews
fuente
Entonces, ¿copia esto en cada archivo de prueba que va a necesitar el recurso?
Andy Hayden
@AndyHayden Dependiendo de cómo escribas tus accesorios, puedes ponerlos en cada archivo de prueba donde los necesites o puedes ponerlos en un archivo conftest.py stackoverflow.com/questions/34466027/…
Everett Toews
2
Sin embargo, esto no es una configuración de clase, ¿verdad? Se ejecutaría antes de cada método de prueba en la clase.
Malhar
1
En este caso particular, solo se ejecuta cuando se usa como parámetro en un método de prueba. por ejemplo, el resourceparam entest_that_depends_on_resource(self, resource)
Everett Toews
64

Cuando escribe "pruebas definidas como métodos de clase" , ¿realmente se refiere a métodos de clase (métodos que reciben su clase como primer parámetro) o simplemente métodos regulares (métodos que reciben una instancia como primer parámetro)?

Dado que su ejemplo se usa selfpara los métodos de prueba, estoy asumiendo el último, por lo que solo necesita usar setup_methoden su lugar:

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

La instancia del método de prueba se pasa a setup_methody teardown_method, pero se puede ignorar si su código de configuración / desmontaje no necesita conocer el contexto de prueba. Puede encontrar más información aquí .

También le recomiendo que se familiarice con los accesorios de py.test , ya que son un concepto más poderoso.

Bruno Oliveira
fuente
1
Los accesorios son más débiles que los métodos de clase: no permiten la destrucción de objetos no creados por ellos (que a menudo es lo que es realmente necesario). Aparte de eso, gracias por la información.
wvxvw
Esto me golpeó mientras actualizaba una base de código desde una versión 3.0.x de pytest hasta una variante 4.x. Algún código antiguo utilizado setup_classcon métodos simulados y similares que debían modernizarse. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos
28

Esto podría ayudar a http://docs.pytest.org/en/latest/xunit_setup.html

En mi suite de pruebas, agrupo mis casos de prueba en clases. Para la configuración y el desmontaje que necesito para todos los casos de prueba en esa clase, utilizo los métodos setup_class(cls)y teardown_class(cls)clases.

Y para la configuración y el desmontaje que necesito para cada uno de los casos de prueba, utilizo setup_method(method)yteardown_method(methods)

Ejemplo:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Ahora, cuando ejecuto mis pruebas, cuando comienza la ejecución de TestClass, registra los detalles de cuándo comienza la ejecución, cuándo finaliza la ejecución y lo mismo para los métodos.

Puede agregar otros pasos de configuración y desmontaje que pueda tener en las ubicaciones respectivas.

¡Espero eso ayude!

Kiran Vemuri
fuente
Hola @Kiran, ¿cuál es la diferencia entre el setup_classvs setup_method?
imsrgadich
1
@imsrgadich Cuando organiza sus casos de prueba en clases, <setup / teardown> _class se utiliza para los pasos de configuración y desmontaje de la clase y <setup / teardown> _method son los pasos respectivos para cada método de caso de prueba.
Kiran Vemuri
1
Maldita sea ... ¡ahora lo entiendo! estuvo atascado en él durante unas horas. Entonces, para poner las cosas en perspectiva. El <setup/teardown>_classpara toda la clase. Aquí, pueden ser cosas como establecer el enlace a la base de datos o cargar el archivo de datos. Y luego, cada caso de prueba puede tener su propia configuración en forma de <setup/teardown>_method. Las cosas están mucho más claras ahora. ¡Muchas gracias!
imsrgadich
24

Como sugirió @Bruno, el uso de accesorios de pytest es otra solución a la que se puede acceder tanto para clases de prueba como para funciones de prueba simples. Aquí hay un ejemplo de prueba de funciones de python2.7 :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Entonces, correr test_1...produce:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Observe que stuff_i_setupse hace referencia en el accesorio, lo que permite que ese objeto sea setupy torn downpara la prueba con la que está interactuando. Puede imaginar que esto podría ser útil para un objeto persistente, como una base de datos hipotética o alguna conexión, que debe borrarse antes de que se ejecute cada prueba para mantenerlos aislados.

ecoe
fuente
13

Su código debería funcionar tal como lo espera si agrega @classmethoddecoradores.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

Ver http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

Okken
fuente
Esto es exactamente lo que aparece en la documentación. El problema que tuve con el doc fue que tuve dificultades para entender el contexto: tradicionalmente se hace referencia a self como self, no cls, así que esto me pareció extraño, fuera del contexto de la clase misma. Kiran (arriba) proporciona este contexto.
Cognitiaclaeves
1
@Cognitiaclaeves "self se conoce tradicionalmente como self, no cls" Sí, selfse usa para métodos de instancia, donde el primer argumento es la instancia de objeto específico en el que se lleva a cabo la operación del método, mientras que clsse usa para @classmethods, que están vinculados a la clase y no una instancia de la clase (es decir, un objeto).
code_dredd