Módulo 8: Proyecto — arma un framework de tests para Reservo

2. La columna de fixtures y la jerarquía de `conftest.py`

Descripción

Empieza el ensamblaje, y empieza por donde debe: la columna vertebral. De todas las capas del framework, la de fixtures es la que sostiene a las demás —la factory produce datos que las fixtures consumen, los tests piden fixtures por nombre, el booking_service que arma el mundo de cada test de integración es una fixture compuesta—. Si esta capa está mal puesta, todo lo que se apoye en ella se tambalea. Por eso es la primera que rearmas, y la que más cuidado de arquitectura pide, porque aquí no solo escribes fixtures: decides dónde vive cada una en la jerarquía de conftest.py, y esa decisión de ubicación es puro diseño.

Al terminar esta lección vas a tener montada la columna completa de Reservo en tres niveles de conftest.py, cada nivel con una responsabilidad clara: la raíz con la infraestructura (los colaboradores y la fixture compuesta booking_service), tests/conftest.py con los datos de dominio (la sala, los socios, la fecha, la reserva pagada), y tests/integration/conftest.py con la única fixture que solo la capa de integración necesita (confirmed_booking, una reserva creada por el servicio real). Y vas a leer el grafo entero instanciándose con pytest --setup-show, la radiografía de la columna que aprendiste a interpretar en el módulo 2. Al final, la primera capa del framework estará atornillada y verificada.

Conexión con el módulo: esta lección coloca la pieza que la lección 1 etiquetó "M2" en el árbol. Es la base física del framework —la 3 le pondrá encima la estructura de carpetas y los marcadores, la 4 la biblioteca de aserciones, la 6 la factory que alimenta estas fixtures de datos—. La frontera con las lecciones vecinas: aquí decidimos dónde vive cada fixture y cómo se compone, no qué datos fabrica (eso es la factory, lección 6) ni qué carpetas la contienen (eso es la lección 3). Y la frontera con el módulo 2 original: allá aprendiste el mecanismo de las fixtures (scope, composición, yield, jerarquía); aquí lo aplicas a la arquitectura concreta del framework, con las decisiones de ubicación como el tema.

Analogía: los tres pisos de un edificio de oficinas

Piensa en un edificio de oficinas de una empresa. No todo vive en el mismo piso, y la razón no es capricho: es que cada cosa sirve a un radio distinto de gente. En la planta baja, junto a la entrada, está lo que todo el mundo usa sin importar a qué área vaya —la recepción, los ascensores, los baños, la cafetería—. Nadie discute que eso va abajo: si lo pusieras en el séptimo piso, la gente del segundo tendría que subir cinco pisos por un café.

En los pisos intermedios está lo que usan muchos departamentos pero no la calle —las salas de juntas compartidas, la sala de impresión, el comedor de empleados—. No van en la planta baja (un visitante no entra a la sala de impresión), pero tampoco pertenecen a un solo departamento: los comparten varios. Y en un piso específico está lo que solo ese departamento necesita —el laboratorio del área de I+D, con su equipo especializado que nadie más toca—. Ponerlo en la planta baja sería absurdo: ocuparía espacio común con algo que solo una décima parte de la empresa usa.

La jerarquía de conftest.py es exactamente ese edificio. El conftest.py de la raíz es la planta baja: ahí va lo que toda la suite usa —los colaboradores de Reservo, el booking_service—, disponible para cualquier test sin que nadie lo importe. El conftest.py de tests/ es el piso intermedio: ahí van los datos de dominio que muchas carpetas comparten —la sala, los socios— pero que no son infraestructura del corredor. Y el conftest.py de tests/integration/ es el piso del departamento: ahí va lo que solo la integración necesita —confirmed_booking, la reserva que pasó por el servicio real—, invisible para los tests unitarios que jamás la piden. Colocar cada fixture en su piso según quién la usa es la decisión de arquitectura de esta lección. La regla se resume en una frase: una fixture vive en el ancestro común más cercano de todos los tests que la usan —ni más arriba (ensuciarías pisos que no la necesitan), ni más abajo (dejarías fuera a tests que sí).

Nivel 1: la raíz — infraestructura y la columna compuesta

Empieza por la planta baja. El conftest.py de la raíz es el hogar de la infraestructura: las piezas que arman el mundo de Reservo y que toda la suite —unit e integration— necesita. Son los cinco colaboradores del BookingService y la fixture compuesta que los une.

# conftest.py  (raíz del proyecto)
from datetime import datetime

import pytest

from reservo.calendar import Calendar
from reservo.doubles import (FakeBookingRepository, FakePaymentGateway,
                             FixedClock, SpyEmailSender)
from reservo.service import BookingService


# --- Colaboradores: cada pieza en su fixture ---
@pytest.fixture
def calendar():
    return Calendar()


@pytest.fixture
def clock():
    # 72h antes del lunes 2026-03-02 09:00, para que cancelar reembolse todo.
    return FixedClock(datetime(2026, 2, 27, 9))


@pytest.fixture
def payments():
    return FakePaymentGateway()


@pytest.fixture
def emails():
    return SpyEmailSender()


@pytest.fixture
def repo():
    return FakeBookingRepository()


# --- La columna compuesta ---
@pytest.fixture
def booking_service(calendar, clock, payments, emails, repo):
    return BookingService(calendar, clock, payments, emails, repo)

Detente en dos decisiones de arquitectura, porque son las que este código toma y que en un examen tendrías que justificar.

Primera: los colaboradores están en fixtures separadas, no armados dentro de booking_service. Podrías haber escrito una sola fixture booking_service que construyera el Calendar, el FixedClock y los demás en su cuerpo. Sería más corto. Pero booking_service los pide como parámetros —los compone (módulo 2, lección 5)—, y eso te compra dos cosas que la fixture-monstruo te negaría. Una: un test puede pedir un colaborador suelto —el payments— para inspeccionarlo (payments.charges), porque es la misma instancia que el servicio usa por dentro (pytest arma cada fixture una vez por test y la comparte). Dos: podrías sobrescribir un colaborador en una carpeta —un payments que falla— sin tocar la definición de booking_service. La composición no es un adorno; es lo que hace inspeccionable y especializable la columna.

Segunda: el clock está fijo en una hora concreta, 72 horas antes del lunes. FixedClock(datetime(2026, 2, 27, 9)) no es una fecha al azar: el lunes de referencia de la suite es 2026-03-02 09:00, y 72 horas antes es exactamente 2026-02-27 09:00. Con el reloj ahí, cuando un test de integración reserve algo para el lunes y lo cancele, la anticipación será de 72 horas y el reembolso será total (6000) —el número-ancla—. Un reloj fijo en vez del reloj real del sistema es lo que hace las pruebas deterministas: dan siempre el mismo resultado, hoy y dentro de un año. Esa hora concreta es una decisión que ata el comportamiento del clock a un número-ancla verificable.

Todas estas fixtures son de scope function (el default): cada test recibe instancias frescas. Y tiene que ser así, no es opcional —los dobles registran estado (payments.charges crece, emails.sent crece, el calendario se llena), y compartirlos entre tests contaminaría unos con los efectos de otros (módulo 2, lección 4)—. La composición ya te ahorró el andamio; no necesitas además subir el scope, y hacerlo aquí compraría bugs que dependen del orden.

Nivel 2: tests/conftest.py — los datos de dominio

Sube al piso intermedio. Los datos de Reservo —la sala, los socios, la fecha de referencia, la reserva pagada— no son infraestructura del corredor: son objetos de dominio que los tests leen y afirman. Muchas carpetas los comparten, así que no van en un solo test; pero tampoco son planta baja. Su hogar es el conftest.py de tests/.

# tests/conftest.py
from datetime import datetime

import pytest

from tests.factories import make_booking, make_member, make_room


@pytest.fixture
def focus_room():
    return make_room()                         # Focus, 2500/h


@pytest.fixture
def ana():
    return make_member()                       # basic


@pytest.fixture
def bruno():
    return make_member(id="m-bruno", name="Bruno", tier="pro")


@pytest.fixture
def monday_9am():
    return datetime(2026, 3, 2, 9)


@pytest.fixture
def paid_booking():
    # Una reserva pro pagada 6000, para los tests de reembolso.
    return make_booking(
        member_id="m-bruno",
        price_cents=6000,
        start=datetime(2026, 3, 2, 9),
        end=datetime(2026, 3, 2, 12),
    )

Fíjate en la decisión de ubicación, que es la del piso intermedio. ¿Por qué estas fixtures no van en la raíz junto a booking_service? Porque hay una diferencia de naturaleza: booking_service y sus colaboradores son infraestructura —la maquinaria de prueba—, mientras que focus_room, ana y paid_booking son datos de dominio —el material sobre el que los tests afirman—. Separarlos en dos niveles de conftest.py no es obligatorio para que funcione (pytest los encontraría igual en la raíz), pero es una decisión de legibilidad: quien abre el conftest.py de la raíz ve la infraestructura del framework; quien abre el de tests/ ve el catálogo de datos de dominio. Cada archivo cuenta una historia coherente, en vez de mezclar hooks, colaboradores y salas en una sola pila.

Y nota algo que conecta con la lección 6: estas fixtures de datos no construyen los objetos a mano —no escriben Room(id="focus", ...) con todos los campos—, sino que los piden a la factory (make_room(), make_member(...), make_booking(...)). La fixture focus_room es un adaptador delgado sobre make_room(): la factory sabe construir la sala; la fixture la ofrece por nombre a los tests. Esa es la conexión entre la capa de fixtures (M2) y la capa de datos (M7): la factory produce, la fixture entrega. Por ahora, make_room y compañía son funciones que existen en tests/factories.py; las construyes a fondo en la lección 6.

Nivel 3: tests/integration/conftest.py — lo que solo un piso usa

Sube al piso del departamento. Hay una fixture que solo los tests de integración necesitan: una reserva que no fue fabricada a mano ni por la factory, sino creada por el servicio real, pasando por todo el flujo —cobrada, guardada, confirmada—. Los tests unitarios jamás la piden (no arman el servicio), así que ponerla en la planta baja ensuciaría el catálogo común con algo de un solo piso. Su hogar es el conftest.py de tests/integration/.

# tests/integration/conftest.py
from datetime import timedelta

import pytest


@pytest.fixture
def confirmed_booking(booking_service, focus_room, bruno, monday_9am):
    # Una reserva que pasó por el flujo real: cobrada y guardada.
    end = monday_9am + timedelta(hours=3)
    return booking_service.book(focus_room, bruno, monday_9am, end)

Esta fixture ilustra dos ideas de arquitectura a la vez.

La dependencia fluye de adentro hacia afuera. confirmed_booking vive en tests/integration/ pero pide booking_service (de la raíz), focus_room, bruno y monday_9am (de tests/conftest.py). Una fixture de un piso interior puede usar fixtures de pisos exteriores —así como la sala del laboratorio de I+D usa los ascensores de la planta baja—. Lo que no puede pasar es al revés: una fixture de la raíz no puede pedir confirmed_booking, porque la raíz no "ve" hacia adentro. La visibilidad es jerárquica: de adentro se ve todo lo de afuera, de afuera no se ve lo de adentro.

Es un dato distinto del paid_booking del piso intermedio, y por eso vive en otro piso. paid_booking (en tests/conftest.py) es una reserva fabricada —la factory la arma con los campos que le decimos, sin pasar por el servicio—; sirve para los tests unitarios de reembolso, que solo necesitan un Booking con cierto precio. confirmed_booking (en tests/integration/conftest.py) es una reserva creada por el flujo real —pasó por booking_service.book, así que se cobró de verdad, se guardó en el repo, disparó el correo—; sirve para los tests de integración de cancelación, que necesitan verificar el flujo completo. Dos reservas, dos naturalezas, dos pisos. Que las dos sean "un Booking" no las hace la misma fixture: una es dato de prueba, la otra es resultado de ejercitar el sistema.

Ejemplo trabajado: leer el grafo de la columna

La columna está montada en tres pisos. Ahora véla instanciándose. Corre uno de los tests de integración —el que usa el servicio compuesto— con --setup-show, la bandera que muestra cada fixture montándose y desmontándose:

python3 -m pytest tests/integration/test_booking_flow.py::test_booking_a_pro_charges_6000 --setup-show

Qué esperar. En mi máquina (Python 3.14.0, pytest 9.1.1), recortando el encabezado y el resumen para ver el grafo:

tests/integration/test_booking_flow.py
        SETUP    F calendar
        SETUP    F clock
        SETUP    F payments
        SETUP    F emails
        SETUP    F repo
        SETUP    F booking_service (fixtures used: calendar, clock, emails, payments, repo)
        SETUP    F focus_room
        SETUP    F bruno
        SETUP    F monday_9am
        tests/integration/test_booking_flow.py::test_booking_a_pro_charges_6000 (fixtures used: booking_service, bruno, calendar, clock, emails, focus_room, monday_9am, payments, repo) .
        TEARDOWN F monday_9am
        TEARDOWN F bruno
        TEARDOWN F focus_room
        TEARDOWN F booking_service
        TEARDOWN F repo
        TEARDOWN F emails
        TEARDOWN F payments
        TEARDOWN F clock
        TEARDOWN F calendar

Este bloque es la columna vertebral hecha visible, y vale la pena leerlo con todo lo que sabes, porque prueba que los tres pisos se conectan:

El orden del SETUP respeta el grafo de dependencias. Los cinco colaboradores —calendar, clock, payments, emails, repo— se arman antes que booking_service, porque él depende de ellos y no puede existir hasta que existan. La línea SETUP F booking_service (fixtures used: calendar, clock, emails, payments, repo) lo dice explícito: pytest te muestra de qué depende el compuesto. Nadie escribió ese orden; pytest lo dedujo del grafo (orden topológico). Los submontajes primero, el ensamble final después.

Las fixtures de los tres pisos aparecen juntas, sin fronteras visibles. En el grafo conviven calendar (raíz), focus_room y bruno (tests/conftest.py), sin que nada marque de qué conftest.py salió cada una. Para el test, la jerarquía es transparente: pide booking_service, focus_room, bruno, monday_9am y payments, y pytest los resuelve buscando hacia afuera desde la carpeta del test. La jerarquía organiza el código fuente (qué archivo contiene qué), no la ejecución (el test las ve todas como un solo espacio de nombres). Esa es la magia del edificio: el visitante usa la recepción, la sala de juntas y el laboratorio sin pensar en qué piso está cada una.

El TEARDOWN corre en orden inverso. booking_service se desmonta antes que sus colaboradores, y calendar (el primero en armarse) es el último en desmontarse —el reverso exacto del armado (LIFO)—. Lo último que se monta es lo primero que se quita, porque las piezas de arriba dependen de las de abajo y no puedes retirar una pieza mientras algo que la usa sigue vivo.

Que este grafo se arme limpio —con confirmed_booking disponible solo aquí, focus_room compartida, booking_service compuesto— es la prueba de que la jerarquía de tres pisos está bien puesta. La columna sostiene la suite.

Errores comunes

Poner todo en el conftest.py de la raíz "para que todo lo alcance". Qué pasa: alguien, para no pensar en la jerarquía, mete las quince fixtures —infraestructura, datos y la de integración— en el conftest.py de la raíz. Funciona: pytest las encuentra todas. Pero el archivo se vuelve una pila de cosas sin historia, y confirmed_booking —que solo la integración usa— queda visible para los tests unitarios que jamás la piden, como un aviso del laboratorio de I+D pegado en la recepción. Por qué pasa: la raíz "alcanza todo", así que parece el lugar seguro. Cómo detectarlo: si tu conftest.py de la raíz tiene fixtures que solo una carpeta usa, o mezcla hooks con salas con dobles sin separación, subiste de más. Cómo corregirlo: aplica la regla del ancestro común —cada fixture en el piso más bajo que alcanza a todos sus usuarios—. La infraestructura común, en la raíz; los datos de dominio, en tests/conftest.py; lo específico de una capa, en el conftest.py de esa carpeta. La jerarquía no es burocracia; es lo que mantiene cada archivo contando una sola historia.

Armar los colaboradores dentro de booking_service en vez de componerlos. Qué pasa: alguien escribe una fixture booking_service que construye el Calendar, el FixedClock y los demás en su cuerpo, sin pedirlos como parámetros. Por qué pasa: se ve más corto tener "todo en una fixture". Cómo detectarlo: si tu fixture booking_service tiene constructores de colaboradores adentro (Calendar(), FakePaymentGateway()) en vez de recibirlos como argumentos, es una fixture-monstruo, no compuesta. Cómo corregirlo: extrae cada colaborador a su fixture y compón. Solo así un test puede pedir payments para inspeccionar payments.charges —el test_booking_a_pro_charges_6000 lo necesita para verificar el cobro—, porque el payments que el test pide es el mismo que el servicio usa. La fixture-monstruo esconde los colaboradores adentro y te niega esa inspección.

Confundir un dato fabricado con un resultado del sistema. Qué pasa: alguien ve que ya tiene paid_booking (una reserva) y decide reusarla también en los tests de integración de cancelación, en vez de crear confirmed_booking. Los tests "funcionan", pero están mintiendo: paid_booking nunca pasó por booking_service.book, así que no se cobró de verdad, no se guardó en el repo, no disparó el correo. Un test de integración que cancela una reserva fabricada no está probando el flujo real. Por qué pasa: las dos son "un Booking", y reusar parece ahorrar. Cómo detectarlo: si un test de integración afirma efectos del flujo (que se guardó, que se cobró, que se envió el correo) sobre una reserva que la factory fabricó en vez de que el servicio creó, está verificando un montaje falso. Cómo corregirlo: distingue las dos naturalezas. El dato fabricado (paid_booking, de la factory) sirve para tests unitarios que solo necesitan un objeto con ciertos campos; el dato resultante (confirmed_booking, del servicio) sirve para tests de integración que verifican el flujo completo. Viven en pisos distintos porque son cosas distintas.

Ejercicios

Ejercicio 1 — Coloca cada fixture en su piso. Para cada fixture, di en cuál de los tres conftest.py va (raíz, tests/, o tests/integration/) y por qué, usando la regla del ancestro común. (a) booking_service, que arma el servicio y usan tanto los tests de integración como un test de humo unitario que quiere inspeccionar un cobro. (b) focus_room, la sala Focus, que usan tests de precio (unit) y de reserva (integration). (c) busy_calendar, un calendario con una reserva confirmada precargada, que solo usan los tests de integración de doble reserva. (d) parsed_config, una fixture que lee la config del framework y la usa un solo test de un archivo tests/integration/test_environment.py.

Ver solución
  • (a) Raíz. La usan tests de dos carpetas distintas (unit e integration), así que su ancestro común es la raíz. Además es infraestructura (la maquinaria de prueba), que es la naturaleza que vive en la planta baja.
  • (b) tests/conftest.py (piso intermedio). La usan unit e integration, cuyo ancestro común es tests/. Y es un dato de dominio (una sala), no infraestructura, así que su hogar natural es el conftest.py de datos, no la raíz. (Funcionaría en la raíz, pero mezclarla con la infraestructura ensucia la historia de ese archivo.)
  • (c) tests/integration/conftest.py (el piso del departamento). Solo la usan tests de integración, así que su ancestro común es tests/integration/. Ponerla más arriba la haría visible para unit, que no la necesita.
  • (d) Depende, pero probablemente tests/integration/conftest.py o el propio archivo. Si un solo test de un solo archivo la usa, el ancestro común es ese archivo —podría vivir como fixture local en test_environment.py mismo—. Si previeras que más tests de integración la van a usar, tests/integration/conftest.py es defendible. La regla: no la subas más arriba de donde de verdad se usa; una fixture que un solo test usa no pertenece a un conftest.py compartido.

La regla mecánica que practicas: encuentra todos los tests que usan la fixture, halla la carpeta ancestro común más cercana, y pon la fixture en el conftest.py de esa carpeta —ni más arriba ni más abajo—. La naturaleza (infraestructura vs datos) desempata entre la raíz y tests/ cuando el ancestro común es la raíz.

Ejercicio 2 — Compón la columna. Tienes las cinco fixtures de colaboradores (calendar, clock, payments, emails, repo) en el conftest.py de la raíz. Escribe la fixture compuesta booking_service que las una, y luego explica por qué un test que pide booking_service y payments recibe el mismo payments que el servicio usa por dentro.

Ver solución

La fixture compuesta pide los cinco colaboradores como parámetros y los pasa al constructor:

@pytest.fixture
def booking_service(calendar, clock, payments, emails, repo):
    return BookingService(calendar, clock, payments, emails, repo)

Por qué el test y el servicio ven el mismo payments: pytest arma cada fixture una sola vez por test y la comparte entre todos sus consumidores dentro de ese test (caché por scope, módulo 2). Cuando un test pide booking_service y payments, pytest arma payments una vez; se lo pasa a booking_service (que lo guarda como su pasarela) y también se lo entrega al test. No son dos pasarelas: es la misma instancia, referenciada desde dos lados. Por eso el test puede hacer booking_service.book(...) y luego verificar payments.charges —está inspeccionando el mismo objeto que el servicio acaba de modificar—. Si booking_service construyera su propio payments adentro (fixture-monstruo), el test vería una pasarela distinta y la inspección fallaría. La composición es lo que garantiza la instancia compartida.

Ejercicio 3 — Predice el grafo. Un test de integración pide confirmed_booking, que a su vez depende de booking_service, focus_room, bruno y monday_9am. Sin correr nada, describe: (a) ¿En qué orden aproximado se arman las fixtures (qué va antes de qué)? (b) ¿Puede confirmed_booking, que vive en tests/integration/conftest.py, depender de booking_service, que vive en la raíz? ¿Y podría booking_service depender de confirmed_booking? (c) ¿En qué orden se desmonta todo?

Ver solución
  • (a) Primero se arman las hojas del grafo, después lo que depende de ellas. booking_service necesita sus cinco colaboradores (calendar, clock, payments, emails, repo), así que esos van primero, luego booking_service. También se arman focus_room, bruno y monday_9am (hojas independientes). Y al final, confirmed_booking, que depende de booking_service, focus_room, bruno y monday_9am —no puede armarse hasta que las cuatro existan—. En --setup-show verías los colaboradores y los datos primero, y confirmed_booking como una de las últimas, con (fixtures used: booking_service, bruno, focus_room, monday_9am).
  • (b) puede: confirmed_booking (piso interior) puede depender de booking_service (piso exterior), porque la visibilidad fluye de adentro hacia afuera —un piso interior ve todo lo de los pisos exteriores—. Lo contrario no: booking_service (raíz) no puede depender de confirmed_booking (integración), porque la raíz no ve hacia adentro. Si lo intentaras, pytest daría un error de fixture no encontrada al correr la suite unitaria, que no tiene confirmed_booking a la vista.
  • (c) En orden inverso al armado (LIFO). confirmed_booking se desmonta primero (fue la última en armarse), luego los datos y booking_service, y los colaboradores al final —calendar, que se armó primero, se desmonta último—. Lo último que entra es lo primero que sale, porque las piezas de arriba dependen de las de abajo.

La lección: el grafo lo deduce pytest de las dependencias que declaras (los parámetros), y la jerarquía de conftest.py solo decide dónde vive el código de cada fixture, no el orden de armado ni la visibilidad hacia afuera.

Resumen y siguiente paso

En esta lección montaste la primera capa del framework: la columna de fixtures en tres niveles de conftest.py. La raíz lleva la infraestructura —los cinco colaboradores y la fixture compuesta booking_service, con el clock fijo 72 horas antes del lunes para que cancelar reembolse el número-ancla—. tests/conftest.py lleva los datos de dominio —focus_room, ana, bruno, monday_9am, paid_booking—, adaptadores delgados sobre la factory. Y tests/integration/conftest.py lleva confirmed_booking, la reserva creada por el servicio real que solo la integración necesita. Cada fixture en el piso del ancestro común más cercano de sus usuarios: ni más arriba (ensuciarías pisos ajenos), ni más abajo (dejarías fuera a quien la usa).

Leíste el grafo entero con --setup-show y comprobaste que los tres pisos se conectan sin costura: los colaboradores antes que booking_service (orden topológico), las fixtures de todos los pisos conviviendo en un solo espacio de nombres para el test (la jerarquía organiza el código, no la ejecución), y el teardown en orden inverso (LIFO). La columna sostiene la suite.

Antes de avanzar deberías poder: decidir en qué conftest.py va una fixture con la regla del ancestro común; distinguir infraestructura (raíz) de datos de dominio (tests/conftest.py) de lo específico de una capa (conftest.py de la carpeta); componer booking_service y explicar por qué el test comparte instancia con el servicio; y leer un --setup-show como el retrato del grafo de tu columna.

Lo que sigue es darle a la columna una forma física y un contrato. En la lección 3 pones la estructura de carpetas —tests/unit/ y tests/integration/ como capas gobernadas por testpaths— y los marcadores: registras smoke, slow e integration en pyproject.toml, activas --strict-markers, y ves la selección -m cortando la suite en subconjuntos. La columna que montaste aquí vivirá dentro de esa estructura, y los marcadores te dejarán correr solo el trozo que quieras.

Recursos

  • pytest — conftest.py: sharing fixtures across files — la referencia de la jerarquía de conftest.py que aplicaste en tres niveles; cómo pytest busca fixtures hacia afuera desde la carpeta del test. En inglés.
  • pytest — Fixtures can request other fixtures — la mecánica de la composición con la que armaste booking_service y confirmed_booking: una fixture nombra otras como parámetros y pytest resuelve el grafo. En inglés.
  • pytest — --setup-show — la bandera que usaste para leer el grafo de la columna instanciándose; correrla sobre cualquier suite ajena es la forma más rápida de entender su arquitectura de fixtures. En inglés.