Práctica 1.6: Uso de pytest
P6 - Funciones y pruebas unitarias con pytest¶
Partimos del entorno de P5. Separar una función de la entrada y la salida permite probar la lógica con datos controlados. Una prueba fallida aporta información: no es un motivo para ocultar el error ni para cambiar la prueba sin analizarlo.
1. Objetivos¶
- Definir funciones reutilizables con parámetros y
return. - Organizar código en
src/y pruebas entests/. - Escribir aserciones con
assert, parametrizar casos y comprobar excepciones. - Ejecutar pytest desde el terminal y desde VS Code e interpretar su informe.
2. Relación con RA1 y criterios de evaluación¶
Esta práctica apoya el RA1, especialmente CE b y CE c, mediante la creación del proyecto y el uso del IDE. Las funciones y las pruebas son una herramienta de desarrollo para demostrar ese trabajo, no un CE adicional.
3. Conceptos previos¶
Una función es un bloque de código reutilizable. Favorece dos ideas importantes:
- Reutilización: la misma función puede llamarse varias veces y desde diferentes partes del programa.
- Modularización: un programa complejo se divide en partes pequeñas que se pueden entender, mantener y probar por separado.
Una definición de función incluye def, un nombre, parámetros opcionales, dos puntos, un bloque indentado y, cuando corresponda, una sentencia return:
Las pruebas unitarias son pequeños programas que permiten comprobar que una pequeña parte de nuestro programa, normalmente una función, se comporta como esperamos. Se ejecutan de forma aislada y repetible.
En proyectos profesionales pueden ejecutarse automáticamente después de cada cambio mediante integración continua; en esta práctica las ejecutarás desde el terminal y desde VS Code.
Por ejemplo, podemos comprobar nuestra función suma() mediante:
La instrucción assert comprueba que una condición sea verdadera. Si alguna no se cumple, la prueba falla.
¿Qué valores debemos probar?
Es importante elegir diferentes situaciones: casos normales, valores límite y casos especiales. Una función que supera una prueba no tiene por qué funcionar correctamente con todos los valores posibles.
Utilizaremos pytest para ejecutar automáticamente estas comprobaciones e identificar los fallos. También veremos cómo probar varios conjuntos de valores mediante @pytest.mark.parametrize.
En esta práctica ejecutaremos las pruebas desde el terminal y desde VS Code. Más adelante profundizaréis en las técnicas de testing en el módulo de Entornos de Desarrollo.
4. Pasos a seguir¶
4.1. Crear una función¶
En src/main.py escribe:
def suma(numero1, numero2):
"""Devuelve la suma de dos números."""
return numero1 + numero2
def dividir(dividendo, divisor):
"""Divide dos números y rechaza la división entre cero."""
if divisor == 0:
raise ValueError("El divisor no puede ser cero")
return dividendo / divisor
def main():
print(f"La suma de 3 + 2 es {suma(3, 2)}")
if __name__ == "__main__":
main()
La función se puede reutilizar con print(suma(3, 2)),
print(suma(25, 40)) o total = 100 + suma(25, 40). No mezcles input() con
la lógica que quieres probar.
También puedes mostrar el resultado con distintas formas de construir el texto:
print("La suma de 3 + 2 es {}".format(suma(3, 2)))
print("La suma de", 3, "+", 2, "es", suma(3, 2))
print("La suma de " + str(3) + " + " + str(2) + " es " + str(suma(3, 2)))
texto = "La suma de "
texto += str(3) + " + " + str(2)
texto += " es " + str(suma(3, 2))
print(texto)
print(f"La suma de 3 + 2 es {suma(3, 2)}")
Estas variantes retoman los contenidos de entrada, conversión y formato de
cadenas trabajados en P2. La prueba, sin embargo, debe comprobar el retorno de
suma, no comparar un texto mostrado por pantalla.
4.2. Escribir y ejecutar la primera prueba¶
En tests/test_main.py escribe:
from src.main import dividir, suma
def test_suma_dos_numeros():
resultado = suma(2, 3)
assert resultado == 5
def test_suma_casos_sencillos():
assert suma(0, 0) == 0
assert suma(100, -100) == 0
Crea un entorno virtual .venv (si aún no lo has creado), actívalo e instala pytest, ya que no viene incluido por defecto en Python:
Comprueba que se ha instalado correctamente:
Si aparece el número de versión de pytest, ya podemos ejecutar nuestras pruebas.
Recuerda: cada entorno virtual tiene sus propias dependencias. Si creas otro entorno virtual, tendrás que instalar pytest nuevamente en él.
Con el entorno activo y desde la raíz del proyecto:
Los archivos test_*.py y las funciones test_* se descubren automáticamente.
Si la importación falla, revisa la raíz desde la que ejecutas pytest y la estructura de carpetas. Puedes añadir __init__.py vacío en src/ y tests/ si tu configuración del IDE lo necesita.
Otra alternativa, especialmente si usas una carpeta src separada, es crear pytest.ini en la raíz:
En ese caso, el import de la prueba puede escribirse como from main import suma. Elige una de las dos configuraciones y mantenla igual en todo el proyecto; no mezcles rutas de importación sin comprobarlas.
Recuerda estas reglas al escribir pruebas:
- importa la función desde el módulo donde está definida;
- nombra las funciones de prueba con el prefijo
test_; - separa cada comportamiento que quieras comprobar en una prueba clara;
- utiliza
assertpara expresar el resultado esperado; - deja que pytest capture y explique los fallos en lugar de ocultarlos.
4.3. Parametrizar y probar errores¶
Amplía tests/test_main.py:
import pytest
from src.main import dividir, suma
@pytest.mark.parametrize(
"primer_sumando, segundo_sumando, esperado",
[(0, 0, 0), (-1, 1, 0), (5, 5, 10), (2.5, 1.5, 4.0)],
)
def test_suma_varios_casos(primer_sumando, segundo_sumando, esperado):
assert suma(primer_sumando, segundo_sumando) == esperado
def test_dividir_rechaza_cero():
with pytest.raises(ValueError, match="divisor"):
dividir(10, 0)
Prueba casos normales, límites, negativos y errores esperados. Para observar un fallo controlado, cambia temporalmente una expectativa, ejecuta pytest, lee el informe y restáurala. No cambies el test para esconder un error del código.
¿VS Code no reconoce la importación de pytest?
Si aparece el aviso Import "pytest" could not be resolved, aunque hayas instalado pytest correctamente, es posible que VS Code esté utilizando un intérprete de Python diferente al de tu entorno virtual.
Para solucionarlo:
- Pulsa
Ctrl + Shift + Py busca Python: Select Interpreter. - Selecciona el intérprete de tu proyecto:
.venv\Scripts\python.exe(Windows) o.venv/bin/python(Linux). - Espera unos segundos y comprueba que desaparece la advertencia.
Recuerda: activar .venv en el terminal no significa necesariamente que VS Code esté utilizando ese mismo intérprete para analizar el código.
4.4. Ejecutar desde VS Code¶
Selecciona el intérprete de .venv, abre la vista Testing, configura pytest
si VS Code lo solicita y ejecuta una prueba o toda la carpeta. Comprueba también
el botón de depuración. El terminal y el IDE deben usar el mismo entorno.
5. Comprobaciones de finalización¶
- Todas las pruebas pasan con
python -m pytest -v. - Se ha visto al menos un caso parametrizado y una excepción esperada.
- El informe permite distinguir
passed,failedyerror. - Una prueba se ejecuta desde la vista Testing de VS Code.
6. Ejercicios y entregables¶
6.1. Comparar números¶
Crea compara_numeros.py y test_compara_numeros.py. La función recibe dos
números y devuelve el mayor, o 0 si son iguales. Prueba números positivos,
negativos e iguales.
6.2. Factorial desde el IDE¶
Crea calcula_factorial.py y sus pruebas. Para entradas de 0 a 10 devuelve
el factorial; para cualquier otro valor devuelve -1. Recuerda que 0! = 1.
Ejecuta las pruebas desde la vista Testing, provoca un error en el código y
documenta la salida antes de corregirlo.
Entrega los módulos, los archivos de prueba, la estructura del proyecto y capturas del terminal y de VS Code con una ejecución correcta y otra fallida.