9. Seleccionar pruebas por archivo, nombre, carpeta y expresión

9.1 Objetivo del tema

En el tema anterior ejecutamos la suite completa. Eso es necesario antes de cerrar un cambio, pero durante el desarrollo muchas veces conviene ejecutar solo una parte de la suite.

En este tema veremos cómo seleccionar pruebas por carpeta, archivo, función, nombre parcial y expresión. Esto permite trabajar más rápido sin perder orden.

Objetivo práctico: ejecutar subconjuntos de pruebas con precisión usando rutas, node ids y expresiones de pytest.

9.2 Cuándo conviene seleccionar pruebas

Seleccionar pruebas es útil cuando estás trabajando sobre una funcionalidad concreta y no necesitas ejecutar toda la suite en cada intento.

Por ejemplo, conviene seleccionar pruebas cuando:

  • Estás corrigiendo una falla específica.
  • Modificaste un módulo pequeño y quieres validar solo sus pruebas.
  • La suite completa tarda demasiado para ejecutarla en cada cambio.
  • Quieres comprobar una prueba nueva mientras la estás escribiendo.
  • Necesitas investigar qué prueba produce un error.

9.3 Ejecutar toda la suite como punto de partida

El comando general sigue siendo:

python -m pytest

Este comando debe usarse con frecuencia, especialmente antes de dar por terminado un cambio. La selección de pruebas ayuda durante el trabajo, pero no reemplaza la ejecución completa.

9.4 Ejecutar una carpeta completa

Para ejecutar todas las pruebas dentro de una carpeta:

python -m pytest tests

Si tienes subcarpetas, puedes apuntar a una de ellas:

python -m pytest tests/unit

pytest ejecutará las pruebas detectadas dentro de esa ruta.

9.5 Ejecutar un archivo de prueba

Para ejecutar solo un archivo:

python -m pytest tests/test_calculadora.py

Esto es útil cuando estás trabajando sobre un módulo específico y quieres validar sus pruebas rápidamente.

9.6 Ejecutar una función de prueba

pytest permite ejecutar una prueba exacta usando el archivo y el nombre de la función separados por :::

python -m pytest tests/test_calculadora.py::test_sumar_devuelve_la_suma_de_dos_numeros

Esta forma se llama node id. Es una identificación precisa de una prueba.

9.7 Obtener nombres exactos de pruebas

Si no recuerdas el nombre exacto de una prueba, puedes listar las pruebas sin ejecutarlas:

python -m pytest --collect-only

pytest mostrará las pruebas detectadas. Eso ayuda a copiar el nombre correcto para usarlo luego con ::.

9.8 Ejecutar por nombre parcial con -k

La opción -k permite seleccionar pruebas por una palabra incluida en el nombre:

python -m pytest -k calculadora

También puedes buscar por comportamiento:

python -m pytest -k descuento

Esto funciona mejor cuando las pruebas tienen nombres descriptivos.

9.9 Combinar palabras con -k

Con -k puedes usar expresiones:

python -m pytest -k "usuario and invalido"

También puedes excluir palabras:

python -m pytest -k "usuario and not invalido"

Y combinar alternativas:

python -m pytest -k "usuario or cupon"

9.10 Crear pruebas para practicar selección

Crea el archivo app/carrito.py:

def calcular_total(productos):
    return sum(producto["precio"] * producto["cantidad"] for producto in productos)


def carrito_esta_vacio(productos):
    return len(productos) == 0

Luego crea tests/test_carrito.py:

from app.carrito import calcular_total, carrito_esta_vacio


def test_calcular_total_con_un_producto_devuelve_precio_por_cantidad():
    productos = [{"precio": 100, "cantidad": 2}]

    assert calcular_total(productos) == 200


def test_calcular_total_con_varios_productos_devuelve_suma_total():
    productos = [
        {"precio": 100, "cantidad": 2},
        {"precio": 50, "cantidad": 3},
    ]

    assert calcular_total(productos) == 350


def test_carrito_esta_vacio_con_lista_vacia_devuelve_true():
    assert carrito_esta_vacio([]) is True


def test_carrito_esta_vacio_con_productos_devuelve_false():
    assert carrito_esta_vacio([{"precio": 100, "cantidad": 1}]) is False

9.11 Ejecutar solo las pruebas del carrito

Ejecuta el archivo completo:

python -m pytest tests/test_carrito.py

Ejecuta solo pruebas que contengan total en el nombre:

python -m pytest tests/test_carrito.py -k total

Ejecuta solo pruebas relacionadas con carrito vacío:

python -m pytest tests/test_carrito.py -k vacio

9.12 Seleccionar y detener en la primera falla

Puedes combinar selección con otras opciones:

python -m pytest tests/test_carrito.py -k total -x

Este comando ejecuta solo pruebas del archivo que coinciden con total y se detiene en la primera falla.

9.13 Seleccionar con salida detallada

También puedes agregar -v para ver cada prueba seleccionada:

python -m pytest tests/test_carrito.py -k total -v

Si -v ya está en pytest.ini, no necesitas repetirlo. Aun así, puede aparecer en comandos de otros proyectos.

9.14 Seleccionar desde el script run_tests.py

Podemos mejorar run_tests.py para aceptar una expresión de búsqueda. Agrega este argumento:

parser.add_argument("--buscar", help="Ejecuta pruebas cuyo nombre coincide con la expresión indicada")

Luego, en construir_comando:

if args.buscar:
    comando.extend(["-k", args.buscar])

Así podrás ejecutar:

python run_tests.py --buscar carrito

9.15 Seleccionar una ruta desde el script

También podemos permitir que el script reciba una ruta opcional:

parser.add_argument("ruta", nargs="?", default=None, help="Archivo o carpeta de pruebas")

Y en construir_comando:

if args.ruta:
    comando.append(args.ruta)

Con esto podrás ejecutar:

python run_tests.py tests/test_carrito.py
python run_tests.py tests/test_carrito.py --buscar total

9.16 Orden de argumentos

Cuando uses un argumento posicional como ruta, conviene escribir primero la ruta y luego las opciones:

python run_tests.py tests/test_carrito.py --buscar total

Esto mejora la lectura del comando, aunque argparse puede aceptar distintas posiciones en muchos casos.

9.17 Script actualizado

La parte de lectura de argumentos puede quedar así:

def leer_argumentos():
    parser = argparse.ArgumentParser(description="Ejecuta la suite automatizada")
    parser.add_argument("ruta", nargs="?", default=None, help="Archivo o carpeta de pruebas")
    parser.add_argument("--buscar", help="Ejecuta pruebas cuyo nombre coincide con la expresión indicada")
    parser.add_argument("--rapido", action="store_true", help="Excluye pruebas marcadas como lentas")
    parser.add_argument("--reporte", action="store_true", help="Genera un reporte HTML")
    parser.add_argument("--detener", action="store_true", help="Detiene la ejecución en la primera falla")
    return parser.parse_args()

Y la construcción del comando:

def construir_comando(args):
    comando = [sys.executable, "-m", "pytest"]

    if args.ruta:
        comando.append(args.ruta)

    if args.buscar:
        comando.extend(["-k", args.buscar])

    if args.rapido:
        comando.extend(["-m", "not lento"])

    if args.detener:
        comando.append("-x")

    if args.reporte:
        REPORTS_DIR.mkdir(exist_ok=True)
        comando.extend([
            "--html=reports/reporte.html",
            "--self-contained-html",
        ])

    return comando

9.18 Cuándo ejecutar solo una parte y cuándo ejecutar todo

Durante el desarrollo, seleccionar pruebas es práctico. Pero antes de cerrar un cambio, conviene ejecutar la suite completa.

  • Durante una corrección puntual: ejecuta una prueba o archivo específico.
  • Después de modificar un módulo: ejecuta las pruebas relacionadas.
  • Antes de terminar el cambio: ejecuta toda la suite.
  • Si aparece una falla inesperada: ejecuta con salida detallada y selección más pequeña.

9.19 Documentar comandos útiles

Agrega al README.md una sección de selección de pruebas:

## Seleccionar pruebas

Ejecutar un archivo:

python -m pytest tests/test_carrito.py

Ejecutar una prueba exacta:

python -m pytest tests/test_carrito.py::test_calcular_total_con_un_producto_devuelve_precio_por_cantidad

Buscar por nombre:

python -m pytest -k carrito

Buscar por expresión:

python -m pytest -k "usuario and invalido"

9.20 Problemas frecuentes

  • No se ejecuta ninguna prueba: revisa el nombre del archivo, función o expresión usada con -k.
  • El node id falla: confirma el nombre exacto con python -m pytest --collect-only.
  • Se ejecutan más pruebas de las esperadas: usa una expresión más específica o una ruta de archivo.
  • La expresión con espacios falla: encierra la expresión entre comillas.
  • Seleccionas siempre muy poco: recuerda ejecutar la suite completa antes de cerrar el cambio.

9.21 Ejercicio práctico

Usando las pruebas de test_carrito.py, ejecuta estos comandos y observa qué pruebas se seleccionan:

python -m pytest tests/test_carrito.py
python -m pytest tests/test_carrito.py -k total
python -m pytest tests/test_carrito.py -k "vacio and true"
python -m pytest --collect-only

Luego agrega al script run_tests.py los argumentos ruta y --buscar si todavía no lo hiciste.

9.22 Solución propuesta

Para ejecutar solo pruebas del carrito que calculan totales:

python -m pytest tests/test_carrito.py -k total

Con el script actualizado:

python run_tests.py tests/test_carrito.py --buscar total

Para ejecutar una prueba exacta:

python -m pytest tests/test_carrito.py::test_calcular_total_con_un_producto_devuelve_precio_por_cantidad

9.23 Lista de verificación

Antes de continuar con el próximo tema, verifica lo siguiente:

  • Puedes ejecutar toda la suite con python -m pytest.
  • Puedes ejecutar una carpeta de pruebas.
  • Puedes ejecutar un archivo específico.
  • Puedes ejecutar una función exacta con ::.
  • Puedes buscar pruebas por nombre con -k.
  • Puedes listar pruebas con --collect-only.
  • El script run_tests.py puede recibir ruta y expresión de búsqueda.

9.24 Conclusión

En este tema aprendimos a seleccionar pruebas por carpeta, archivo, función exacta, nombre parcial y expresión. Esta habilidad hace más eficiente el trabajo diario con suites automatizadas.

La selección de pruebas debe usarse para acelerar el desarrollo, pero no debe reemplazar la ejecución completa de la suite. En el próximo tema trabajaremos con marcadores en pytest para clasificar pruebas automatizadas.