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

9 minutos de lectura

avatar de usuario
Avasin

Estoy usando selenio para pruebas de extremo a extremo y no puedo entender cómo usarlo setup_class y teardown_class métodos.

Necesito configurar el navegador en setup_class método, luego realice un montón de pruebas definidas como métodos de clase y finalmente cierre el navegador en teardown_class método.

Pero lógicamente parece una mala solución, porque de hecho mis pruebas no funcionarán con clase, sino con objeto. paso self param dentro de cada método de prueba, por lo que puedo 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 la clase. Debería crearse para cada objeto por separado, ¿verdad?

Entonces, necesito usar __init__ y __del__ métodos en lugar de setup_class y teardown_class?

avatar de usuario
Everett Toews

De acuerdo a Finalización de accesorios/ejecución de código de desmontajela mejor práctica actual para la configuración y el desmontaje es usar yield en vez 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 request-objeto de contexto en su función de dispositivo y llamando a su request.addfinalizer mé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))

  • Entonces, ¿copias esto en cada archivo de prueba en el que vas a necesitar el recurso?

    –Andy Hayden

    11 de noviembre de 2017 a las 2:00

  • 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

    15/11/2017 a las 17:00

  • En este caso particular, solo se ejecuta cuando se usa como parámetro en un método de prueba. por ejemplo, el resource param en test_that_depends_on_resource(self, resource)

    – Everett Toew

    16 de noviembre de 2017 a las 17:34

  • Tenga en cuenta que puede establecer el alcance del dispositivo en ‘clase’ y el uso automático en verdadero para asegurarse de que el código se llame una vez para cada clase sin tener que incluirlo como parámetro en ninguna llamada de prueba: “` pytest.fixture(scope=” class”, autouse=True) def resource(): print(“setup”) yield “resource” print(“teardown”) “`

    – Chris

    1 oct 2020 a las 12:14

  • Actualización del enlace: docs.pytest.org/en/stable/…

    – Silvano

    19/04/2021 a las 11:00

avatar de usuario
Bruno Oliveira

Cuando escribes “pruebas definidas como métodos de clase”¿realmente quieres decir métodos de clase (métodos que reciben su clase como primer parámetro) o simplemente métodos regulares (métodos que reciben un instancia como primer parámetro)?

Dado que su ejemplo utiliza self para los métodos de prueba, asumo lo último, por lo que solo necesita usar setup_method en cambio:

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_method y teardown_method, pero puede ignorarse si su código de configuración/desmontaje no necesita conocer el contexto de prueba. Se puede encontrar más información aquí.

También te recomiendo que te familiarices con py.test’s accesoriosya que son un concepto más potente.

  • 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 realmente es necesario). Aparte de eso, gracias por la información.

    – wvxvw

    25 de noviembre de 2018 a las 12:14

  • Esto me ocurrió mientras actualizaba una base de código de una versión 3.0.x de pytest a una variante 4.x. Se usa un código más antiguo setup_class con métodos burlados y similares que necesitaban ser modernizados. setup_class(self, foo, bar) –> setup_method(self,function,foo,bar)

    – jxramos

    24 de febrero de 2020 a las 18:03

avatar de usuario
Kiran Vemuri

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

En mi conjunto 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, uso el setup_class(cls) y teardown_class(cls) métodos de clase.

Y para la configuración y el desmontaje que necesito para cada uno de los casos de prueba, uso el setup_method(method) y teardown_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!

  • Hola @Kiran, ¿cuál es la diferencia entre el setup_class contra setup_method?

    – Sr. Posterior no normalizado

    10 de septiembre de 2019 a las 13:02

  • @imsrgadich Cuando organiza sus casos de prueba en clases, _class se usa para los pasos de configuración y desmontaje de la clase y _method son los pasos respectivos para cada método de caso de prueba.

    – Kiran Vemuri

    12 de septiembre de 2019 a las 5:04

  • ¡Maldita sea… ahora lo entiendo! estuvo atascado en él durante unas horas. Entonces, para poner las cosas en perspectiva. los <setup/teardown>_class para toda la clase. Aquí, pueden ser cosas como establecer un 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!

    – Sr. Posterior no normalizado

    12 de septiembre de 2019 a las 11:07


avatar de usuario
ecoe

Como sugirió @Bruno, el uso de accesorios de pytest es otra solución accesible para ambas clases de prueba o incluso para funciones de prueba simples. Aquí hay un ejemplo que prueba las 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, corriendo test_1... produce:

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

Darse cuenta de stuff_i_setup se hace referencia en el accesorio, lo que permite que ese objeto sea setup y torn down para 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.

Su código debería funcionar tal como lo espera si agrega @classmethod decoradores

@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/

  • Esto es más o menos exactamente lo que aparece en la documentación. El problema que tuve con el documento fue que tuve dificultades para entender el contexto: tradicionalmente se hace referencia a self como self, no cls, por lo que me pareció extraño, fuera del contexto de la clase misma. Kiran (arriba) proporciona este contexto.

    – Claves cognitivas

    17 de abril de 2019 a las 16:41

  • @Cognitiaclaeves “el self se conoce tradicionalmente como self, no cls” Sí, self se utiliza para métodos de instancia, donde el primer argumento es la instancia de objeto específica en la que se lleva a cabo la operación del método, mientras que cls se utiliza para @classmethods, que están vinculados a la clase y no a una instancia de la clase (es decir, un objeto).

    – código_dredd

    6 de agosto de 2019 a las 21:16

avatar de usuario
Kiran Sk

import pytest
class Test:
    @pytest.fixture()
    def setUp(self):
        print("setup")
        yield "resource"
        print("teardown")

    def test_that_depends_on_resource(self, setUp):
        print("testing {}".format(setUp))

Para ejecutar:

pytest nam_of_the_module.py -v 

  • Esto es más o menos exactamente lo que aparece en la documentación. El problema que tuve con el documento fue que tuve dificultades para entender el contexto: tradicionalmente se hace referencia a self como self, no cls, por lo que me pareció extraño, fuera del contexto de la clase misma. Kiran (arriba) proporciona este contexto.

    – Claves cognitivas

    17 de abril de 2019 a las 16:41

  • @Cognitiaclaeves “el self se conoce tradicionalmente como self, no cls” Sí, self se utiliza para métodos de instancia, donde el primer argumento es la instancia de objeto específica en la que se lleva a cabo la operación del método, mientras que cls se utiliza para @classmethods, que están vinculados a la clase y no a una instancia de la clase (es decir, un objeto).

    – código_dredd

    6 de agosto de 2019 a las 21:16

avatar de usuario
Ápterix

No estoy seguro de haber obtenido los detalles sobre el uso de Selenium en sus preguntas originales, pero en caso de que simplemente estuviera preguntando cómo usar un estilo de configuración/desmontaje más clásico, Pytest es compatible con la mayoría de las funciones de prueba unitaria, por lo que podría hacer algo como:

import unittest


class TestHello(unittest.TestCase):

    def setUp(self):
        print('running setUp')

    def test_one(self):
        print('running test_one')

    def test_two(self):
        print('running test_two')

    def tearDown(self):
        print('running tearDown')

Que produce:

$ pytest -s -v
====================== test session starts =======================
platform linux -- Python 3.8.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /gnu/store/nckjv3ccwdi6096j478gvns43ssbls2p-python-wrapper-3.8.2/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/tmp/test/.hypothesis/examples')
rootdir: /tmp/test
plugins: hypothesis-5.4.1
collected 2 items                                                

test_hw.py::TestHello::test_one running setUp
running test_one
running tearDown
PASSED
test_hw.py::TestHello::test_two running setUp
running test_two
running tearDown
PASSED

  • Creo que debería ser setup en vez de setUpal menos para pytest-7.1.2.

    – shad96

    9 de julio a las 21:37

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad