El módulo doctest en Python encuentra texto que parece una sesión interactiva dentro de docstrings o archivos de documentación, ejecuta cada comando y compara la salida real con la documentada. Convierte ejemplos en documentación ejecutable y ayuda a mantener tutoriales, ayuda y snippets sincronizados con el código.
Funciona mejor con ejemplos pequeños, deterministas y fáciles de leer. No sustituye una suite completa, especialmente para estados complejos, red, concurrencia, fixtures elaboradas o reglas que necesitan aserciones detalladas.
Escribe el primer ejemplo
Una docstring puede incluir prompts >>> seguidos de la salida esperada.
def doble(valor):
"""Devuelve el doble.
>>> doble(4)
8
>>> doble(-3)
-6
"""
return valor * 2El texto sigue las convenciones de una sesión interactiva. La salida comienza inmediatamente después del comando y termina ante otro prompt o una línea vacía.
Ejecuta con testmod
if __name__ == "__main__":
import doctest
doctest.testmod()Al ejecutar el archivo, no ver salida significa que todos los ejemplos pasaron. Usa modo detallado para observar cada intento:
python modulo.py -vEn CI, comprueba el resultado o integra doctest con unittest para que una configuración incorrecta no parezca éxito.
Usa la línea de comandos
python -m doctest modulo.py
python -m doctest -v modulo.pyPasar un miembro de paquete como archivo aislado puede romper imports relativos. En paquetes, suele ser mejor importarlo dentro del contexto correcto.
Prueba archivos de documentación
testfile() lee ejemplos interactivos de un archivo textual.
import doctest
resultado = doctest.testfile(
"guia.txt",
module_relative=False,
)
print(resultado)El archivo se trata como una docstring grande, lo que permite verificar tutoriales sin colocar todos los ejemplos en el código.
Comprende el contexto
Cada docstring recibe normalmente una copia superficial de los globals del módulo. Ejemplos dentro de la misma docstring comparten nombres; docstrings diferentes normalmente no.
def ejemplo_estado():
"""
>>> valores = [1, 2]
>>> valores.append(3)
>>> valores
[1, 2, 3]
"""Una copia superficial todavía comparte objetos mutables referenciados. Evita estado global real y limpia archivos, variables y conexiones.
Proporciona globals explícitos
globs y extraglobs preparan nombres.
doctest.testfile(
"ejemplos.txt",
globs={"Cliente": Cliente},
extraglobs={"config": config_prueba},
module_relative=False,
)No inyectes credenciales de producción, sesiones reales ni recursos irreversibles. Usa fakes y directorios temporales.
Documenta excepciones
Un ejemplo puede incluir el traceback esperado. Las líneas intermedias suelen ignorarse y se compara el tipo y detalle.
def dividir(a, b):
"""
>>> dividir(10, 0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
"""
return a / bSi el mensaje cambia entre versiones, IGNORE_EXCEPTION_DETAIL permite centrarse en el tipo. Úsalo solo cuando el texto no sea parte del contrato.
Normaliza espacios
NORMALIZE_WHITESPACE considera equivalentes las secuencias de espacios y saltos.
>>> print(list(range(10))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4,
5, 6, 7, 8, 9]Aplica la directiva solo donde el formato sea irrelevante. Activarla globalmente puede ocultar regresiones.
Usa ELLIPSIS con cuidado
ELLIPSIS permite que ... coincida con cualquier fragmento.
>>> objeto
<MiObjeto id=...> # doctest: +ELLIPSISMantén prefijos y sufijos estables. Una elipsis demasiado amplia puede aceptar una salida equivocada.
Representa líneas vacías
Una línea vacía termina la salida esperada. Para exigir una línea en blanco, escribe <BLANKLINE>.
>>> print("primera\n\ntercera")
primera
<BLANKLINE>
terceraEsta regla causa muchos fallos al copiar salida multilínea.
Evita orden no determinista
No compares sets o mappings cuyo orden no sea parte de la API.
>>> sorted(obtener_tags())
['api', 'python', 'prueba']Formatea floats explícitamente. Evita timestamps, direcciones de memoria, UUID aleatorios, rutas temporales y mensajes dependientes de plataforma.
Añade ejemplos con __test__
Un mapping __test__ permite añadir casos que no aparecen en la ayuda principal.
__test__ = {
"casos_extra": """
>>> doble(0)
0
""",
}Los valores pueden ser strings, funciones o clases y sus docstrings se examinan.
Qué objetos se buscan
testmod() examina docstring del módulo, funciones, clases, métodos y objetos de __test__. Los objetos importados desde otros módulos no se buscan automáticamente.
La guía de pydoc en Python explica la visualización de documentación. Para listar clases y funciones sin importar, consulta pyclbr en Python.
Integra con unittest
DocTestSuite() convierte ejemplos de un módulo en una suite unittest.
import doctest
import unittest
import mi_modulo
def load_tests(loader, tests, pattern):
tests.addTests(doctest.DocTestSuite(mi_modulo))
return tests
if __name__ == "__main__":
unittest.main()DocFileSuite() hace lo mismo para archivos de texto. Así la ejecución y los reportes quedan integrados.
Prepara y limpia recursos
Las suites aceptan setUp y tearDown.
def preparar(test):
test.globs["repositorio"] = RepositorioTemporal()
def limpiar(test):
test.globs["repositorio"].close()
suite = doctest.DocTestSuite(
mi_modulo,
setUp=preparar,
tearDown=limpiar,
)La limpieza debe ocurrir incluso tras fallos. Prefiere tempfile, mocks y recursos en memoria.
FAIL_FAST y reportes
La opción -f activa FAIL_FAST. Otras flags muestran diferencias unificadas, de contexto o dentro de líneas.
python -m doctest -v -f guia.txtFallar pronto ayuda durante depuración. En CI puede ser mejor informar todos los ejemplos rotos.
Omite casos deliberadamente
>>> abrir_navegador() # doctest: +SKIPSKIP sirve para ejemplos ilustrativos o servicios no disponibles. Revisa los skips porque pueden ocultar documentación rota.
Analiza ejemplos programáticamente
DocTestParser extrae objetos Example.
from doctest import DocTestParser
texto = """
>>> 2 + 2
4
"""
ejemplos = DocTestParser().get_examples(texto)
print(ejemplos[0].source, ejemplos[0].want)DocTestFinder, DocTestRunner y OutputChecker permiten integraciones personalizadas. Usa la API básica salvo necesidad real.
Seguridad
Doctest ejecuta los ejemplos encontrados. Nunca ejecutes documentación enviada por usuarios, paquetes desconocidos o repositorios no confiables en el proceso principal.
Usa un proceso o contenedor restringido, timeout, límites de memoria y CPU, sin credenciales de producción, con filesystem temporal y red controlada. Importar el módulo ya puede ejecutar código superior.
Tracebacks y diagnósticos
La guía de traceback en Python explica el formato de excepciones. En doctest, incluye solo detalles estables y evita rutas y líneas.
Introspección y documentación
inspect en Python ayuda a recuperar firmas y docstrings antes de construir suites. La introspección de objetos vivos suele exigir importar el código.
Cuándo no usar doctest
- Flujos largos con fixtures complejas.
- Salidas grandes o inestables.
- Pruebas de concurrencia y tiempo.
- Integraciones reales de red y base de datos.
- Property testing con muchos valores.
- Reglas críticas con aserciones detalladas.
Usa unittest, pytest o herramientas especializadas y deja doctest para ejemplos públicos.
Buenas prácticas
- Mantén ejemplos cortos y deterministas.
- Prueba comportamiento público.
- Ordena colecciones antes de mostrarlas.
- Formatea floats explícitamente.
- Usa directivas localmente.
- Evita elipsis excesivas.
- Ejecuta documentación en CI.
- Aísla documentación no confiable.
Conclusión
doctest en Python mantiene ejemplos sincronizados y convierte documentación en pruebas ejecutables. Es excelente para APIs pequeñas, tutoriales y regresiones legibles cuando la salida es estable.
Consulta la documentación oficial de doctest y la documentación de unittest.







