python unit-testing testing global-variables py.test

python - cómo compartir una variable entre módulos para todas las pruebas en py.test



unit-testing testing (4)

Tengo varias pruebas ejecutadas por py.test que se encuentran en múltiples clases en múltiples archivos.

¿Cuál es la forma más sencilla de compartir un diccionario grande, que no quiero duplicar, con cada método de cada clase en cada archivo para ser utilizado por py.test?

En resumen, necesito hacer una "variable global" para cada prueba. Fuera de py.test, no tengo uso para esta variable, por lo que no quiero almacenarla en los archivos que se prueban. Hice un uso frecuente de los accesorios de py.test, pero esto parece excesivo para esta necesidad. Tal vez es la única manera?


Tener un gran diccionario de globales que usa cada prueba es probablemente una mala idea. Si es posible, sugiero refactorizar sus pruebas para evitar este tipo de cosas.

Dicho esto, así es como lo haría: definir un accesorio de autouso que agregue una referencia al diccionario en el espacio de nombres global de cada función.

Aquí hay un código. Todo está en el mismo archivo, pero puede mover el dispositivo a conftest.py en el nivel superior de sus pruebas.

import pytest my_big_global = {''key'': ''value''} @pytest.fixture(autouse=True) def myglobal(request): request.function.func_globals[''foo''] = my_big_global def test_foo(): assert foo[''key''] == ''value'' def test_bar(): assert foo[''key''] == ''bar''

Aquí está el resultado de cuando ejecuto este código:

$ py.test test_global.py -vv ======================================= test session starts ======================================= platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python collected 2 items test_global.py:9: test_foo PASSED test_global.py:12: test_bar FAILED ============================================ FAILURES ============================================= ____________________________________________ test_bar _____________________________________________ def test_bar(): > assert foo[''key''] == ''bar'' E assert ''value'' == ''bar'' E - value E + bar test_global.py:13: AssertionError =============================== 1 failed, 1 passed in 0.01 seconds ===============================

Tenga en cuenta que no puede usar un dispositivo de ámbito de sesión porque entonces no tiene acceso a cada objeto de función. Debido a esto, me aseguro de definir mi gran diccionario global una vez y usar referencias a él; si definiera el diccionario en esa declaración de asignación, se realizaría una nueva copia cada vez.

Para terminar, hacer algo como esto es probablemente una mala idea. Buena suerte sin embargo :)


Mencionas la opción obvia y menos mágica: usar un accesorio. Puede aplicarlo a módulos completos utilizando pytestmark = pytest.mark.usefixtures(''big_dict'') en su módulo, pero no estará en su espacio de nombres, así que explícitamente solicite que sea lo mejor.

Alternativamente, puede asignar cosas al espacio de nombre pytest utilizando el gancho:

# conftest.py def pytest_namespace(): return {''my_big_dict'': {''foo'': ''bar''}}

Y ahora tienes pytest.my_big_dict . El accesorio es probablemente aún más agradable.


Hay toneladas de cosas que me encantan de py.test, pero una cosa que ODIO es lo mal que juega con las herramientas de inteligencia de código. No estoy de acuerdo con que un dispositivo de autouso para declarar una variable sea el método "más claro" en este caso porque no solo desconcierta por completo a mi intérprete de líneas, sino también a cualquier otra persona que no esté familiarizada con el funcionamiento de py.test. Hay mucha magia allí, imo.

Por lo tanto, una cosa que puede hacer que no haga que su linter explote y no requiera TestCase, es crear un módulo llamado globals. Dentro de este módulo, inserte los nombres de las cosas que desea que sean globales {} o None e importe el módulo global en sus pruebas. Luego, en su archivo conftest.py, use los ganchos py.test para establecer (o restablecer) su (s) variable (s) global (es) según corresponda. Esto tiene la ventaja de proporcionarle el trozo con el que trabajar cuando construya las pruebas y los datos completos para las pruebas en tiempo de ejecución.

Por ejemplo, puede usar el gancho pytest_configure() para establecer su dict correctamente cuando se inicia py.test. O bien, si quería asegurarse de que los datos fueran prístinos entre cada prueba, podría usar automáticamente un accesorio para asignar su variable global a su estado conocido antes de cada prueba.

# globals.py my_data = {} # Create a stub for your variable # test_module.py import globals as gbl def test_foo(): assert gbl.my_data[''foo''] == ''bar'' # The global is in the namespace when creating tests # conftest.py import globals as gbl my_data = {''foo'': ''bar''} # Create the master copy in conftest @pytest.fixture(autouse=True) def populate_globals(): gbl.my_data = my_data # Assign the master value to the global before each test

Otra ventaja de este enfoque es que puede usar la sugerencia de tipo en su módulo global para darle el código completo en los objetos globales en su prueba, que probablemente no es necesario para un dict, pero lo encuentro útil cuando estoy usando un objeto ( como webdriver). :)


Actualización: desplácese hacia abajo para un mejor enfoque.

Además de una mejor opción específica de Pytest sugerida por @flub, puede crear una clase TestCase base, definir una variable de clase y subclasificar la clase base en cada prueba que tenga:

import unittest class BaseTestCase(unittest.TestCase): my_dict = {''hello'': ''world''} class TestCase1(BaseTestCase): def test_my_dict_key(self): self.assertTrue(''hello'' in self.my_dict) class TestCase2(BaseTestCase): def test_my_dict_value(self): self.assertTrue(''world'' in self.my_dict.values())