Una parte fundamental del desarrollo de software es la realización de pruebas para garantizar que el código y la solución implementada funcionen según lo esperado. Es esencial probar tu solución y llegar al nivel más detallado posible para asegurar una cobertura de código adecuada, una métrica que evalúa qué porcentaje de tu código ha sido efectivamente probado.
El objetivo es alcanzar una alta cobertura de código, lo que te permitirá afirmar con confianza que el código ha sido sometido a pruebas rigurosas. Para ello, se utilizan pruebas unitarias. Estas pruebas unitarias validan unidades individuales de código, sin depender de otros componentes. Al verificar la funcionalidad de la unidad más pequeña, puedes identificar y resolver problemas rápidamente, evitando que se conviertan en errores mayores.
Sin embargo, en nuestro proyecto, tendremos algunas partes de aislamiento, supongamos que nuestra aplicación se conecta a un servicio externo, que puede ser el API de un tercero o un servicio cloud como AWS, para obtener información de una entidad, para el ejemplo propuesto, queremos obtener el valor del tipo de cambio de euros a dólares. Si escribe una prueba unitaria para un código que llama a un API de cambio de divisas, necesitaría leer datos que devuelve el servicio. Estos datos no son una entrada controlada.
Podría tener datos que no anticipa, si la información no está en un estado estable conocido, y su prueba podría fallar. Pero ese error no indica un problema con el código, sino con el estado de la información, que no es lo que estás intentando probar con una prueba unitaria.
Es importante aislar el código que escribiste lo máximo posible, de manera que tus pruebas unitarias no estén verificando la integración con el servicio externo, sino que se centren exclusivamente en la funcionalidad de tu código. Una forma eficaz de lograr esto es mediante el uso de mocking, donde simulas las respuestas que recibirías de los servicios externos. Esto garantiza que las respuestas sean consistentes y controladas, permitiéndote aislar la lógica de negocio en tu función y detectar posibles errores.
Por ejemplo, si estás trabajando con un API externa, podrías simular sus respuestas, codificando ciertos datos estáticos e inyectándolos en tu código. De este modo, evitarías realizar llamadas reales al API, utilizando en su lugar datos predefinidos. Además, es útil escribir pruebas unitarias que contemplen casos extremos, como cuando el API devuelve un error. En lugar de forzar manualmente el API a un estado de error, puedes simular ese comportamiento para comprobar cómo tu código lo maneja.
Sin embargo, aunque el uso de mocks es valioso, es igualmente importante que parte de las pruebas se realicen con llamadas reales a las APIs, para asegurarte de que el código también funcione correctamente en un entorno real.
Esto es crucial porque los mocks se ejecutan en el entorno local, ya sea en la computadora portátil del desarrollador o en una máquina de compilación, y ese entorno puede estar configurado de manera diferente al de la nube. Estas diferencias podrían influir en el comportamiento del código, por lo que es fundamental realizar pruebas en un entorno real para garantizar que funcione correctamente bajo las condiciones reales de producción.
Por lo tanto, es esencial realizar pruebas tanto de la lógica de negocio como en la nube. La razón es que no se debe depender únicamente de los mocks de servicios en la nube para validar la correcta integración de estos servicios. Aunque es válido utilizar mocks para probar funcionalidades internas, como la lógica de negocio, también es necesario realizar pruebas directas sobre los servicios en la nube para asegurarse de que la configuración y la implementación funcionen correctamente.
Los mocks son especialmente valiosos en las pruebas unitarias, ya que permiten ejecutar numerosos casos de prueba de manera eficiente y repetida. Sin embargo, su utilidad disminuye en pruebas de integración, ya que implementar simulaciones para múltiples puntos de conexión requiere un mayor esfuerzo. Por ello, los mocks deben emplearse principalmente cuando se busca aislar la unidad de código más pequeña. A medida que se avanza hacia pruebas de integración o de extremo a extremo (E2E), los mocks pierden efectividad, ya que se necesita validar el funcionamiento real del sistema en su conjunto.
¿Deseas recibir contenido como este a tu correo electrónico?
En las pruebas de extremo a extremo (E2E), no se deben utilizar mocks, ya que estas pruebas lidian con estados y lógica compleja que no pueden ser replicados fácilmente por marcos de simulación. Estas pruebas buscan verificar el funcionamiento completo del sistema, lo que requiere interacciones reales entre los componentes.
En resumen, aunque las pruebas unitarias pueden parecer más laboriosas al principio, en realidad te ahorran tiempo al acortar el ciclo de retroalimentación. Te permiten identificar y corregir errores rápidamente, lo que resulta más eficiente que esperar los resultados del equipo de QA para detectar problemas. Implementar pruebas unitarias no solo acelera el proceso de desarrollo, sino que también contribuye a entregar código de mayor calidad de forma más ágil.
Ejercicio: Pruebas Unitarias y Mock sobre conversión de divisas
El siguiente ejercicio consiste en una función que recibe como parámetro de entrada la cantidad de dinero en euros y deberá devolver su equivalente en dólares. Para calcular esta conversión, se realizará la llamada a una API externa para obtener el tipo de cambio de la divisa.
Para el ejercicio, necesitamos instalar dos paquetes en nuestro entorno virtual.
pip install requests
pip install pytest
Archivo settings.py
API_FOREIGN_EXCHANGE = "https://api.exchangerate-api.com/v4/latest"
Archivo app.py
import requests
from settings import API_FOREIGN_EXCHANGE
def convert_euros_to_dollar(amount: float) -> float:
"""
Función para convertir euros a dolares.
"""
# Llamada a una API para obtener el tipo de cambio EUR -> USD
api_endpoint: str = "{}/EUR".format(API_FOREIGN_EXCHANGE)
response = requests.get(api_endpoint)
if response.status_code != 200:
raise Exception("No se pudo obtener el tipo de cambio")
data = response.json()
# Obtenemos el tipo de cambio de EUR a USD
rate = data["rates"]["USD"]
# Devolvemos la cantidad en dólares
return round(amount * rate, 2)
Archivo: test_app.py
import pytest
from unittest.mock import patch
from app import convert_euros_to_dollar
@patch('requests.get')
def test_convert_euros_to_dollars(mock_get):
"""
Prueba unitaria para convertir euros a dólares
"""
# Simulamos la respuesta de la API con un tipo de cambio ficticio
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {
"rates": {
"USD": 1.15 # Ejemplo: 1 EUR = 1.15 USD
}
}
amount: float = 100 # cantidad de euros
expected_result: float = 115.0 # resultado esperado
result: float = convert_euros_to_dollar(amount)
# Verificar que el resultado sea el esperado (100 EUR * 1.15 = 115 USD)
assert result == expected_result
@patch('requests.get')
def test_convert_euros_to_dollars_error_api(mock_get):
"""
Prueba unitaria para simular un fallo en la API
"""
# Simulamos un error de la API (HTTP Code 500)
mock_get.return_value.status_code = 500
# Ejecutar la función y verificar que se lanza una excepción
with pytest.raises(Exception, match="No se pudo obtener el tipo de cambio"):
convert_euros_to_dollar(100)
Ejecutamos el archivo de pruebas unitarias
pytest test_app.py
Resultado:
================================ test session starts ==============================
platform Linux -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
cachedir: .pytest_cache
collected 2 items
test_app.py::test_convert_euros_to_dollars PASSED [ 50%]
test_app.py::test_convert_euros_to_dollars_error_api PASSED [100%]
================================== 2 passed in 0.49s ================================
Repositorio Github: https://github.com/Solo-Codigo-Web/mock_foreign_exchange
Publicaciones que te podrían interesar:
Taller de Ejercicios: 11 características y funciones muy utilizadas en la programación con python
Medir la Efectividad y Eficiencia de tu proceso de Continuous Integration y Continuous Deployment
Optimizar operaciones de listas en Python
API de cambio de divisas: exchangerate-api.com