Caso de estudio: Máximo de una lista en Python con Hypothesis
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
r8vnhill/python-dibs
En este ejemplo, queremos probar una función biggest
que toma una lista de enteros y devuelve el valor máximo. Para este caso, definiremos una propiedad que verifique que el valor máximo de una lista es igual al último elemento de la lista una vez ordenada.
🔢 Propiedades y Generación de Datos
Con Hypothesis, podemos definir estrategias que generen listas de enteros y escribir tests que verifiquen las propiedades esperadas de nuestra función biggest
. Comenzamos creando un test para asegurar que, en una lista no vacía, el valor máximo devuelto por biggest
coincida con el último elemento de la lista cuando esta se encuentra ordenada.
from hypothesis import given
from hypothesis.strategies import integers, lists
from testing.pbt.biggest.src.biggest import biggest
@given(lists(integers(), min_size=1))
def test_biggest_with_sorted_list(lst: list[int]) -> None:
assert biggest(lst) == sorted(lst)[-1]
def test_biggest_with_empty_list() -> None:
assert biggest([]) == float('-inf')
@given(lists(integers(), min_size=1))
: Hypothesis genera listas aleatorias de enteros y las pasa como argumento a la prueba. Esto permite verificar el comportamiento de la función con una gran variedad de entradas sin necesidad de escribir casos de prueba manualmente.- Exploración de casos límite: Hypothesis no solo genera listas de tamaño arbitrario, sino que también detecta automáticamente casos límite como listas con un solo elemento o listas con valores extremos, lo que ayuda a descubrir posibles errores en la implementación.
- Manejo de listas vacías: Como
max([])
en Python lanza una excepción,biggest([])
está diseñado para devolverfloat('-inf')
, asegurando un comportamiento predecible y evitando errores de ejecución en escenarios donde la lista es vacía.
🛠️ Implementación de la Función biggest
La función biggest
toma una lista de enteros y devuelve el valor máximo. En este ejemplo, biggest
retorna -inf
cuando recibe una lista vacía, usando reduce
para encontrar el valor máximo en otros casos.
from functools import reduce
def biggest(lst: list[int]) -> int | float:
return reduce(lambda acc, number: number if number > acc else acc, lst, float('-inf'))
📊 Resumen comparativo
Característica | Hypothesis | Kotest |
---|---|---|
Estrategias de generación de datos | Proporciona estrategias para generar múltiples tipos de datos, incluyendo listas, enteros, y cadenas. | Similar a Hypothesis, permite generar datos de distintos tipos para pruebas, aunque con mayor personalización. |
Integración de propiedades en tests | Tests basados en propiedades se integran de forma fluida con decoradores, como @given , y estrategias. | También permite tests basados en propiedades usando checkAll , con un diseño cohesivo para BDD. |
Manejo de casos especiales | Proporciona mecanismos automáticos para listas vacías o valores extremos en datos generados. | Kotest permite configuración en generadores, pero requiere especificaciones más detalladas en algunos casos. |
Personalización de estrategias | Permite definir estrategias avanzadas mediante combinaciones y composición de estrategias. | Ofrece una flexibilidad comparable, pero requiere más configuraciones manuales para generar ciertos datos complejos. |
⚖️ Beneficios y Limitaciones de Hypothesis
Beneficios
- Automatización en generación de datos: Hypothesis genera automáticamente una amplia gama de valores de entrada, cubriendo casos normales y extremos sin requerir definiciones manuales para cada uno.
- Reproducción de errores: Cuando una prueba falla, Hypothesis guarda el caso de prueba que generó el error, facilitando la depuración mediante la reutilización del caso exacto.
- Amplia compatibilidad en estrategias: Hypothesis tiene estrategias listas para una variedad de tipos de datos, lo que permite probar funciones que usan listas, enteros, strings, etc., de manera rápida y sencilla.
Limitaciones
- Control limitado sobre datos complejos: Aunque Hypothesis es potente para casos simples, la configuración de estrategias avanzadas o específicas puede volverse tediosa, en particular para objetos personalizados complejos.
📌 Conclusiones
En esta lección, exploramos cómo utilizar Hypothesis para realizar pruebas basadas en propiedades (Property-Based Testing, PBT) en Python. A través del caso de estudio de la función biggest
, demostramos cómo generar datos automáticamente y verificar propiedades generales del código en lugar de depender de pruebas manuales.
🔑 Puntos clave
- Pruebas basadas en propiedades:
- En lugar de definir casos específicos de prueba, usamos estrategias para generar datos aleatorios y verificar que se cumplan ciertas propiedades de la función bajo prueba.
- Generación automática de datos:
- Hypothesis permite definir estrategias que producen una amplia variedad de entradas, lo que ayuda a detectar errores en escenarios inesperados.
- Detección de casos límite:
- Hypothesis explora automáticamente situaciones extremas, como listas vacías o valores atípicos, lo que mejora la cobertura de las pruebas sin esfuerzo adicional.
- Manejo de listas vacías:
- Implementamos
biggest([]) == float('-inf')
para evitar excepciones y garantizar un comportamiento predecible en escenarios con entradas vacías.
- Implementamos
- Comparación con Kotest:
- Hypothesis y Kotest ofrecen enfoques similares para PBT, pero mientras Hypothesis automatiza la detección de casos límite, Kotest permite mayor personalización en la generación de datos.
✅ Reflexión final
El Property-Based Testing con Hypothesis proporciona una forma escalable y eficiente de validar funciones en Python, asegurando robustez y fiabilidad en el código. Aunque puede requerir configuraciones adicionales para datos complejos, su capacidad para descubrir errores ocultos lo convierte en una herramienta valiosa en el desarrollo de software.
Este enfoque es especialmente útil en librerías de software y proyectos donde la validación exhaustiva de funciones es crítica. Aplicar PBT en tu flujo de trabajo te ayudará a mejorar la calidad del código y reducir la probabilidad de errores inesperados.