Pytest-django: ¿Cómo usar la marca django_db en el accesorio de sesión?

Creado en 14 sept. 2017  ·  15Comentarios  ·  Fuente: pytest-dev/pytest-django

Tengo una situación en la que necesito cargar un accesorio de base de datos enorme creado por una función. El accesorio es necesario en todas las pruebas de API. Así que hice un accesorio de sesión en conftest.py que lo haría. Pero el problema es que pytest arroja la siguiente excepción a pesar de que he marcado django_db :

E Failed: Database access not allowed, use the "django_db" mark to enable it.
A continuación se muestra mi fragmento de código.

from permission.helpers import update_permissions

pytestmark = [
        pytest.mark.django_db(transaction = True),]

@pytest.fixture(scope="session", autouse = True)
def permission(request):
        load_time_consuming_db_fixture()
db-configuration enhancement

Comentario más útil

Este ha sido el punto más problemático para mí desde las pruebas basadas en la clase UnitTest de Django hasta pytest-django; en Django usamos setUpTestData para ejecutar costosas operaciones de base de datos una vez (equivalente a accesorios de pytest con ámbito de sesión), y luego hay un ingenioso truco para ejecutar obj.refresh_from_db() en setUp para actualizar las referencias de clase.

Incluso si solo estoy creando una instancia de modelo de base de datos y recargándola en cada TC, esto casi siempre es más rápido que crear en cada caso de prueba.

Sería genial si pudiéramos fusionar los accesorios del ámbito de la sesión de pytest-tipsi-django aguas arriba, si esa es una posibilidad; tomó un poco de excavación para encontrar este problema y solución.

Todos 15 comentarios

@ludbek también nos hemos perdido esa función y hemos creado un complemento para esta función:
https://github.com/tipsi/pytest-tipsi-django (uso: https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
En conjunto con:
https://github.com/tipsi/pytest-tipsi-testing
Le brinda la capacidad de anidar transacciones y corregir el orden de ejecución/cierre.

@cybergrind Gracias por responder. Definitivamente lo revisaré y te contaré cómo me fue.

Este ha sido el punto más problemático para mí desde las pruebas basadas en la clase UnitTest de Django hasta pytest-django; en Django usamos setUpTestData para ejecutar costosas operaciones de base de datos una vez (equivalente a accesorios de pytest con ámbito de sesión), y luego hay un ingenioso truco para ejecutar obj.refresh_from_db() en setUp para actualizar las referencias de clase.

Incluso si solo estoy creando una instancia de modelo de base de datos y recargándola en cada TC, esto casi siempre es más rápido que crear en cada caso de prueba.

Sería genial si pudiéramos fusionar los accesorios del ámbito de la sesión de pytest-tipsi-django aguas arriba, si esa es una posibilidad; tomó un poco de excavación para encontrar este problema y solución.

hola @pauliplady

No estoy seguro de que el enfoque de pytest-tipsi-djanjo se ajuste al modelo de prueba habitual para pytest . La diferencia más notable es el acabado de las luminarias: actualmente pytest no termina explícitamente las luminarias innecesarias con un alcance más amplio. Por lo tanto, debe finalizar explícitamente las transacciones en un orden particular y, en general, esto puede causar efectos muy diferentes (actualmente pytest puede mantener los accesorios activos incluso si la prueba activa y sus accesorios no lo requieren en absoluto).

Tuvimos que cambiar las pruebas en nuestro proyecto a este enfoque porque a veces necesitamos probar algunos escenarios grandes y hemos reemplazado la gestión manual de transacciones existente en pruebas grandes con accesorios ligeramente mejores, pero aún requiere atención en las pruebas de pedidos.

En este momento solo puedo ver una solución para eso: poner algún tipo de preguntas frecuentes en la documentación.

Gracias por el detalle adicional @cybergrind. He profundizado un poco más en esto, pero se me acabó el tiempo por hoy; aquí es donde debo hacerlo, agradecería una verificación de cordura sobre si este enfoque es útil o no, ya que estoy no tan familiarizado con las partes internas de pytest.

Además, no entiendo lo que quiere decir con "pytest no termina explícitamente los accesorios innecesarios con un alcance más amplio", ¿podría ampliar eso un poco más, por favor? ¿Se refiere a los finalizadores? Eso podría afectar lo que he escrito a continuación.

El complemento pytest-django usa la marca django_db , que se maneja en _django_db_marker en plugin.py (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py #L375), llamando al accesorio db con ámbito de función (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142). Este dispositivo crea una instancia de Django TestCase y llama a su función _pre_setup (y pone en cola _post_teardown ).

Puedo ver un par de opciones:

Me pregunto si podríamos extender _django_db_marker para hacer clases o sesiones opcionalmente.
configuración de alcance también, que haría esencialmente lo mismo que db , pero llamando al equivalente de cls.setUpTestData o una función pasada en la marca kwargs en su lugar.

Para el ámbito de clase, y asumo que también para el ámbito de sesión, el comportamiento esperado sería revertir la base de datos después, por lo que básicamente necesitamos dispositivos de ámbito que se activen en el orden correcto, y que cada uno configure su propio atómico. transacción. Creo que eso significa que esto debe ser una modificación del accesorio db , en lugar de un accesorio separado que se ejecuta junto a él.

De esa manera, activaríamos correctamente cualquier configuración de nivel de clase/sesión que se especifique, y esa configuración se llamaría una vez por clase/sesión. Creo que se requiere una modificación en la marca en sí porque si solo configura una función de ámbito de sesión que activa manualmente django_db_blocker.unblock() , eso parece suceder después de que la marca django_db haya configurado la primera transacción.

Esto podría verse así (en plugin.py _django_db_marker() ):

    if marker:
        if marker.session_db:
            getfixturevalue(request, 'db_session')

        if marker.class_db:
            getfixturevalue(request, 'db_class')

        validate_django_db(marker)
        if marker.transaction:
            getfixturevalue(request, 'transactional_db')
        else:
            getfixturevalue(request, 'db')

¿Es esta una locura o vale la pena seguir explorando este hilo?

Con respecto a la finalización: https://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

Esta prueba no funciona sin una finalización explícita, al igual que los elementos fijos de la base de datos que no son de nivel funcional. Y se trata de la implementación de pytest, por lo que no hay nada que hacer en pytest-django para solucionarlo.

Duplicado de #388 y #33

Gracias, cerrando.

33 está cerrado, y #388 no contiene ninguna discusión significativa (a diferencia de esta). Parece extraño cerrar este @blueyed , en todo caso, sugeriría cerrar el #388 y hacer de este el ticket canónico para este problema.

👍 gracias!

También agradecería mucho esta funcionalidad.

@mkokotovich
¿Cuánto cuesta? ¿Suficiente para hacer que suceda usted mismo? ;)

De todos modos, el problema principal/fundamental aquí (del comentario original) ya es que la base de datos se restablece durante las pruebas, por lo que no hay una forma trivial de tener un accesorio de ámbito de sesión como ese.

Sin embargo, lo que podría funcionar es algo a lo largo de:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

La idea es envolver todo en un bloque atómico adicional. Sin embargo, esto no se ha probado y es posible que deba usar TransactionTestCase para esto.

@pauliplady
Su comentario suena bien, es decir, vale la pena seguir explorando AFAICS (ver también mi comentario anterior).

@blueyed estamos usando ese enfoque durante más de un año (envuelva todo en un bloque atómico adicional) funciona bastante bien.
Pero no puede simplemente agregar algo así porque pytest no tiene el determinista donde se cerrará el nivel de sesión (y otro más alto que la prueba), por lo que requerirá el seguimiento de las dependencias de las transacciones de db independientemente de que se requieran entre sí o no directamente .
Por lo tanto, debe realizar un seguimiento explícito de la pila de transacciones y cerrar las transacciones anidadas antes de la próxima prueba. Se puede hacer de esta manera: https://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

lo siento, quiero aclarar ya que creo que me estoy encontrando con el mismo problema:

Si quiero crear un accesorio que se base en la creación de un nuevo objeto db, pensé que podría hacerlo

@pytest.mark.django_db(transaction=True)
@pytest.fixture(scope="session")
def object():
    object = Object.create_object(params)
    yield object
    // or alternatively
    object = mixer.blend('myObject')
    yield object

Sin embargo, recibo el siguiente error en la ejecución del caso de prueba: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

¿Fue útil esta página
0 / 5 - 0 calificaciones