Módulo 7: Datos y aislamiento en integración
7. Independientes y repetibles
Descripción
Todo el módulo apuntaba a dos propiedades que ahora vamos a nombrar y demostrar, porque son el criterio con el que se juzga si una suite de integración está bien hecha. La primera es la independencia: cada test parte de un estado conocido y no depende de qué otro test corrió antes ni después. Un test independiente da el mismo resultado lo pongas donde lo pongas en la suite, porque trae su propio mundo —lo siembra, lo aísla— y no hereda nada de sus vecinos. La segunda es la repetibilidad: cada test da el mismo resultado cada vez que lo corres, hoy y mañana, en tu máquina y en la de al lado. Un test repetible no depende de la hora, del orden, de datos que quedaron de una corrida anterior, ni de nada que varíe entre ejecuciones. Las dos juntas son lo que hace que un verde signifique algo: si tu suite es independiente y repetible, un verde es una afirmación sólida sobre el código; si no lo es, un verde es una moneda al aire que salió cara esta vez.
Estas dos propiedades no son ideales abstractos: son la consecuencia directa de todo lo que hiciste en las lecciones anteriores. El aislamiento —rollback o base nueva por test— es lo que produce la independencia: cada test empieza limpio porque nadie le dejó estado. El sembrado explícito es lo que hace ese estado conocido en vez de solo limpio. Y determinismo en lo demás —el reloj fijo con FixedClock, los dobles en vez de servicios externos que varían— es lo que produce la repetibilidad. Este módulo, visto desde arriba, era una máquina para fabricar tests independientes y repetibles. En esta lección lo compruebas: vas a correr la suite aislada en orden normal, en orden invertido, y dos veces seguidas, y a ver el mismo verde en los tres casos. Ese "mismo verde siempre" es la firma de una suite sana, la imagen opuesta al flaky dependiente del orden de la lección 2.
Conexión con el módulo: las lecciones 3 a 6 te dieron las herramientas —aislar, elegir recurso, sembrar—; esta nombra el para qué y lo verifica. Es el cierre conceptual antes del mini-proyecto: independencia y repetibilidad son los dos criterios con los que la lección 8 te pedirá juzgar tu propia suite. La independencia es lo contrario exacto de la contaminación que diagnosticaste en la lección 2; la repetibilidad es lo contrario del flaky. Si entiendes que estas dos propiedades se logran con aislamiento más determinismo, y sabes demostrarlas corriendo la suite en varios órdenes, tienes el criterio completo para escribir integración que no miente. La lección 8 lo pone en práctica de punta a punta.
Analogía: la receta que sale igual para cualquiera
Piensa en la diferencia entre una receta de cocina bien escrita y las "instrucciones" de un cocinero que improvisa. La receta bien escrita es independiente y repetible: cada paso empieza de un estado que la propia receta estableció —"precalienta el horno a 180°", no "usa el horno como quedó"—, y no depende del orden en que hiciste otras recetas ese día. Cualquiera que la siga, en cualquier cocina, con los ingredientes listados, obtiene el mismo pan. Si le das la receta a diez personas distintas, salen diez panes iguales. Eso es una receta en la que puedes confiar: el resultado depende de la receta, no de quién ni cuándo ni en qué orden.
Las "instrucciones" del cocinero que improvisa son lo contrario: "agrega sal al gusto", "cocina hasta que se vea bien", "aprovecha el sofrito que quedó de antes". El resultado depende de cosas que varían —el gusto de quién, qué quedó de antes, el orden del día—, así que dos personas siguiendo lo mismo obtienen platos distintos, y la misma persona obtiene platos distintos en días distintos. No puedes confiar en que "salió bien" signifique algo, porque la próxima vez puede salir mal sin que cambiaras nada. Una suite de integración es una receta: si cada test establece su propio estado (independiente) y no depende de nada que varíe (repetible), su verde es confiable, como el pan que sale igual para cualquiera. Si depende del orden, de datos heredados o de la hora, su verde es el "se ve bien" del que improvisa: cierto hoy, quién sabe mañana.
Independencia: el mismo verde en cualquier orden
Tomemos la suite aislada —tres tests de integración de Reservo, cada uno con su base fresca por fixture— y probemos su independencia de la forma más directa: corriéndola en dos órdenes distintos y comprobando que el veredicto no cambia.
# tests/test_suite_isolated.py — suite de integracion AISLADA
import sqlite3
import pytest
from datetime import datetime
from reservo.calendar import Calendar
from reservo.doubles import FixedClock, SpyEmailSender, StubPaymentGateway
from reservo.models import Member, Room
from reservo.services import BookingService
from reservo.sqlite_repo import SqliteBookingRepository
FOCUS = Room("focus", "Focus", 4, 2500); ANA = Member("m-ana", "Ana", "pro")
@pytest.fixture
def repo():
conn = sqlite3.connect(":memory:") # base fresca por test
yield SqliteBookingRepository(conn)
conn.close()
def service(repo):
return BookingService(Calendar(), FixedClock(datetime(2026, 3, 1, 9)),
StubPaymentGateway(True), SpyEmailSender(), repo)
def test_book_creates_one_focus_booking(repo):
service(repo).book(FOCUS, ANA, datetime(2026, 3, 10, 9), datetime(2026, 3, 10, 12))
assert len(repo.find_by_room("focus")) == 1
def test_focus_is_empty_before_my_booking(repo):
assert len(repo.find_by_room("focus")) == 0 # base nueva: es verdad
service(repo).book(FOCUS, ANA, datetime(2026, 3, 11, 9), datetime(2026, 3, 11, 12))
assert len(repo.find_by_room("focus")) == 1
def test_exactly_one_confirmed_total(repo):
service(repo).book(FOCUS, ANA, datetime(2026, 3, 12, 9), datetime(2026, 3, 12, 12))
assert len(repo.find_by_room("focus")) == 1 # su propia reserva, nada mas
Primero, el orden de definición.
Qué esperar. En mi máquina (Python 3.14.0, pytest 9.1.1):
python3 -m pytest tests/test_suite_isolated.py -v
============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0
collected 3 items
tests/test_suite_isolated.py::test_book_creates_one_focus_booking PASSED [ 33%]
tests/test_suite_isolated.py::test_focus_is_empty_before_my_booking PASSED [ 66%]
tests/test_suite_isolated.py::test_exactly_one_confirmed_total PASSED [100%]
============================== 3 passed in 0.01s ===============================
Ahora, el orden invertido, listando los tests al revés en la línea de comandos:
python3 -m pytest tests/test_suite_isolated.py::test_exactly_one_confirmed_total \
tests/test_suite_isolated.py::test_focus_is_empty_before_my_booking \
tests/test_suite_isolated.py::test_book_creates_one_focus_booking -v
============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0
collected 3 items
tests/test_suite_isolated.py::test_exactly_one_confirmed_total PASSED [ 33%]
tests/test_suite_isolated.py::test_focus_is_empty_before_my_booking PASSED [ 66%]
tests/test_suite_isolated.py::test_book_creates_one_focus_booking PASSED [100%]
El mismo verde, invertido el orden. Compáralo con la suite compartida de la lección 1, donde invertir el orden cambiaba cuál test fallaba: allí el veredicto dependía del orden, aquí no. Esa insensibilidad al orden es la independencia, medida de forma empírica: no la afirmas leyendo el código, la compruebas corriéndolo de varias maneras y viendo que el resultado no se mueve. Cada test parte de su propia base fresca, así que quién corrió antes es irrelevante; el resultado depende solo de lo que cada test hace.
Repetibilidad: el mismo verde cada vez
La independencia responde "¿importa el orden?"; la repetibilidad responde "¿importa la corrida?". Un test repetible da el mismo resultado si lo corres ahora y de nuevo dentro de un segundo, sin cambiar nada. Comprobémoslo corriendo la misma suite dos veces seguidas.
Qué esperar. En mi máquina (Python 3.14.0, pytest 9.1.1):
python3 -m pytest tests/test_suite_isolated.py -v # primera corrida
collected 3 items
tests/test_suite_isolated.py::test_book_creates_one_focus_booking PASSED [ 33%]
tests/test_suite_isolated.py::test_focus_is_empty_before_my_booking PASSED [ 66%]
tests/test_suite_isolated.py::test_exactly_one_confirmed_total PASSED [100%]
============================== 3 passed in 0.01s ===============================
python3 -m pytest tests/test_suite_isolated.py -v # segunda corrida, sin cambiar nada
collected 3 items
tests/test_suite_isolated.py::test_book_creates_one_focus_booking PASSED [ 33%]
tests/test_suite_isolated.py::test_focus_is_empty_before_my_booking PASSED [ 66%]
tests/test_suite_isolated.py::test_exactly_one_confirmed_total PASSED [100%]
============================== 3 passed in 0.01s ===============================
Idéntico. Y esto, que parece trivial, no lo es: es trivial porque hicimos el trabajo. La suite es repetible por dos razones que vale la pena separar. Primera, el aislamiento: cada corrida arranca sin datos heredados de la anterior, porque las bases :memory: de la corrida previa se destruyeron con sus conexiones —no quedó un test.db en disco con las reservas de ayer que hiciera fallar la corrida de hoy—. Segunda, el determinismo de lo demás: el reloj está fijado con FixedClock(datetime(2026, 3, 1, 9)), así que cancel y los cálculos de tiempo dan lo mismo sin importar cuándo corras; el pago es un StubPaymentGateway que no llama a ningún servicio externo que pueda variar; el correo es un SpyEmailSender que no manda nada real. Nada en la suite depende de algo que cambie entre corridas.
Si cualquiera de esas dos piezas fallara, la repetibilidad se rompería. Un test.db compartido en disco haría que la segunda corrida heredara datos de la primera. Un datetime.now() real en vez del FixedClock haría que un test de reembolso diera distinto según la fecha en que lo corras —el clásico test que pasa hoy y falla el mes que viene—. La repetibilidad es el resultado de haber quitado, uno por uno, todos los caminos por los que una corrida podía influir en otra o el mundo exterior podía colarse.
Por qué el orden nunca debe importar
Detengámonos en la afirmación fuerte de esta lección: en una suite bien hecha, el orden de ejecución nunca debe cambiar un veredicto. No es una preferencia estética; es una condición de que los tests signifiquen algo. Si el veredicto depende del orden, entonces "verde" no es una afirmación sobre el código —es una afirmación sobre el código más el orden en que corrieron los tests hoy—, y ese orden puede cambiar por mil razones fuera de tu control: pytest reordena, alguien agrega un test, corres un subconjunto, una herramienta baraja el orden a propósito para cazar justo estos problemas.
De hecho, esa última es una práctica real: existen herramientas —como pytest-randomly— que barajan el orden de los tests en cada corrida precisamente para exponer dependencias ocultas de orden. En una suite independiente, barajar no cambia nada: verde siempre. En una suite contaminada, barajar hace que a veces pase y a veces falle, revelando el problema que un orden fijo escondía. Que tu suite sobreviva a correr en cualquier orden no es un lujo: es la prueba de que sus verdes son confiables. La forma manual de esa prueba es la que hiciste arriba —correr en orden invertido— y es suficiente para detectar la mayoría de las dependencias; la automática las caza todas, corrida tras corrida.
La conexión con el módulo entero se cierra aquí. La lección 2 te mostró la enfermedad: una suite cuyo veredicto dependía del orden, el flaky de estado compartido. Las lecciones 3 a 6 te dieron la cura: aislar para que cada test parta limpio, sembrar para que parta de un estado conocido, determinismo para que no dependa de nada que varíe. Y esta lección te da la prueba de que la cura funcionó: corre la suite en varios órdenes y varias veces, y si el verde no se mueve, tienes independencia y repetibilidad. Ese es el estándar de una suite de integración en la que se puede confiar.
Errores comunes
Confiar en que "pasa" equivale a "está bien" sin probar el orden. Qué pasa: la suite pasa en el orden de siempre y se da por buena. Por qué pasa: localmente los tests corren casi siempre en el mismo orden, que puede ocultar la dependencia. Cómo detectarlo: corre en orden invertido o baraja con una herramienta; si algún veredicto cambia, la suite no era independiente, solo tenía suerte con el orden. Cómo corregirlo: haz de correr-en-otro-orden parte de tu definición de "verde confiable"; un verde que solo se sostiene en un orden no es confiable.
Dejar un datetime.now() real y romper la repetibilidad. Qué pasa: un test de cancel usa la hora real en vez del FixedClock, y pasa hoy pero fallará cuando la fecha cambie la ventana de reembolso. Por qué pasa: usar la hora real parece inofensivo. Cómo detectarlo: si un test depende de "cuánto falta para el start" y no fija el reloj, su resultado cambia con el calendario. Cómo corregirlo: fija el tiempo con un reloj controlado (FixedClock), como en toda la suite; el determinismo temporal es parte de la repetibilidad.
Creer que aislar la base ya garantiza repetibilidad. Qué pasa: se aísla la base con una fixture perfecta, pero un test sigue siendo flaky porque depende del reloj o de un orden de resultados no garantizado. Por qué pasa: se piensa que el estado de la base es la única fuente de variación. Cómo detectarlo: si la base está aislada y aun así el resultado varía, busca otras fuentes —tiempo, orden de una lista sin ORDER BY, aleatoriedad—. Cómo corregirlo: independencia y repetibilidad necesitan aislamiento más determinismo en todo lo demás; la base es una fuente de variación, no la única.
Ejercicios
Ejercicio 1 — Independiente, repetible, ambas o ninguna. Para cada test, di qué propiedad le falta (independencia, repetibilidad, ambas o ninguna) y por qué: (a) un test que asume que otro test sembró una reserva antes; (b) un test que usa datetime.now() para calcular el reembolso; (c) un test con base fresca por fixture y FixedClock; (d) un test que ordena resultados con find_by_room y asume que salen en un orden específico sin un ORDER BY.
Ver solución
- (a) Le falta independencia. Depende de que otro test corriera antes y dejara la reserva; si cambia el orden o ese test no corre, falla. Es dependencia de orden, lo contrario de independiente. (Repetible sí podría ser, si el orden fuera siempre el mismo, pero es frágil.)
- (b) Le falta repetibilidad.
datetime.now()cambia con el calendario, así que la ventana de reembolso (72 h / 36 h / 12 h antes delstart) da distinto según cuándo corras; pasa hoy y falla otro día sin que cambies nada. (Independiente sí puede ser; el problema es el tiempo, no los vecinos.) - (c) Ninguna le falta: es independiente y repetible. Base fresca por fixture → no hereda de vecinos (independiente); reloj fijo y sin fuentes externas variables → mismo resultado siempre (repetible). Es el estándar sano.
- (d) Le falta repetibilidad (y quizá independencia). Sin
ORDER BY, el orden en que SQLite devuelve las filas no está garantizado y puede variar; un test que asume un orden específico puede pasar o fallar de forma no determinista. Se arregla ordenando explícitamente en la consulta o comparando como conjunto (set), no como lista ordenada.
La regla: independencia es no depender de otros tests; repetibilidad es no depender de nada que varíe entre corridas (tiempo, orden no garantizado, aleatoriedad, servicios externos). Una suite sana tiene las dos.
Ejercicio 2 — Diseña la prueba de independencia sin leer el código. Te entregan una suite de integración de 50 tests y te piden certificar que es independiente, pero no tienes tiempo de leer los 50. Describe dos corridas que, comparadas con la corrida normal, te darían evidencia fuerte de independencia (o revelarían que no la hay), y di qué esperarías ver en una suite sana.
Ver solución
Dos corridas, ambas comparadas contra la corrida normal (que supongamos verde):
-
Orden invertido o barajado. Corre la suite con los tests en otro orden —invertido a mano, o barajado con una herramienta como
pytest-randomly—. En una suite independiente, el resultado es idéntico: los mismos 50 verdes. Si algún test cambia de veredicto (pasa a fallar, o al revés), tienes prueba de dependencia de orden: ese test asume algo de un vecino. -
Subconjuntos aislados. Corre grupos pequeños de tests por separado —o incluso cada test solo— y compara con su resultado dentro de la suite completa. En una suite independiente, cada test da el mismo veredicto solo que en grupo. Si alguno pasa solo pero falla en la suite (o al revés), hay contaminación: depende de qué corrió junto a él.
En una suite sana esperarías que ninguna de las dos corridas cambie un solo veredicto respecto a la normal: mismos verdes en cualquier orden y en cualquier agrupación. Lo potente de estas dos pruebas es que certifican independencia por comportamiento, sin leer los 50 tests: si el resultado es invariante al orden y al agrupamiento, los tests no dependen unos de otros; si varía, sabes exactamente dónde mirar.
Ejercicio 3 — La suite que pasa hoy y fallará en junio. Un test de cancel verifica que cancelar con 60 horas de anticipación devuelve el reembolso total (6000), y calcula la anticipación con now = datetime.now() contra un start fijo en el futuro cercano. Hoy pasa. Explica por qué fallará en algún momento futuro, qué propiedad viola, y cómo lo arreglas sin cambiar lo que el test quiere probar.
Ver solución
Fallará porque now = datetime.now() avanza con el calendario, mientras que el start del test está fijo. Hoy, la distancia entre now y ese start es de 60 horas (más de 48), así que cae en la ventana de reembolso total y el test ve 6000: pasa. Pero a medida que pasan los días reales, datetime.now() se acerca al start fijo: llega un momento en que la anticipación baja de 48 horas (reembolso a la mitad, 3000), luego de 24 (sin reembolso, 0), y el test que esperaba 6000 falla, sin que nadie tocara una línea. Es el clásico test que pasa hoy y falla en junio.
La propiedad que viola es la repetibilidad: su resultado depende de cuándo se ejecuta, algo que varía entre corridas. No es independencia —no depende de otros tests—; es que el tiempo real se coló como entrada oculta.
El arreglo, sin cambiar lo que prueba: fija el reloj. En vez de datetime.now(), usa un now explícito y controlado —el FixedClock(NOW) de toda la suite— con NOW a exactamente 60 horas antes del start. Así la anticipación es siempre 60 horas, caiga cuando caiga la fecha real, y el test verifica lo mismo —reembolso total a más de 48 horas— pero de forma determinista, corras cuando corras. El tiempo deja de ser una entrada del mundo y pasa a ser un dato fijo del test.
Resumen y siguiente paso
En esta lección nombraste y demostraste las dos propiedades que todo el módulo perseguía. La independencia —cada test parte de un estado conocido, sin depender de sus vecinos— la comprobaste corriendo la suite aislada en orden normal y en orden invertido y viendo el mismo verde: la insensibilidad al orden que la suite compartida de la lección 1 no tenía. La repetibilidad —el mismo resultado cada vez— la comprobaste corriendo la suite dos veces con salida idéntica, y viste que se apoya en dos patas: el aislamiento (nada se hereda entre corridas) y el determinismo de lo demás (el FixedClock, los dobles sin servicios externos). Y entendiste por qué el orden nunca debe cambiar un veredicto, y cómo herramientas que barajan el orden convierten esa exigencia en una prueba automática.
Antes de avanzar deberías poder: distinguir independencia de repetibilidad y decir qué le falta a un test que viola cada una; certificar independencia corriendo la suite en varios órdenes y agrupaciones; y reconocer las fuentes de no repetibilidad —tiempo real, orden no garantizado, servicios externos— y cómo neutralizarlas.
Lo que sigue es juntar todo en una sola entrega. En la lección 8, el mini-proyecto, vas a tomar una suite de integración de Reservo que falla por estado compartido —con veredictos distintos según el orden, como la de la lección 1— y a convertirla en una suite aislada y repetible: verde en cualquier orden y cuantas veces la corras. Vas a entregar las dos versiones —la contaminada en rojo y la aislada en verde—, elegir la técnica de aislamiento adecuada y justificarla. Es la síntesis de las siete lecciones y el cierre del módulo, con todo lo que aprendiste puesto a trabajar sobre un caso completo.
Recursos
- Documentación de pytest — Buenas prácticas (tests independientes) — la referencia oficial sobre organizar tests de forma que sean independientes y ejecutables en aislamiento, el fundamento de la independencia de esta lección.
pytest-randomly— el complemento que baraja el orden de los tests en cada corrida para exponer dependencias ocultas de orden; la versión automática de la prueba de independencia que aquí hiciste a mano invirtiendo el orden.- Martin Fowler — Eradicating Non-Determinism in Tests — el análisis clásico de las fuentes de no determinismo (estado compartido, tiempo, orden) y por qué la repetibilidad es innegociable, el marco conceptual de esta lección.
test-failure-diagnosis-guide— la guía hermana del diagnóstico, donde el flaky dependiente del orden es un caso central; independencia y repetibilidad son, vistas desde aquí, la ausencia de ese flaky.