Contenido del curso

Mocks para APIs externas en Python

Resumen

Cuando tu aplicación depende de APIs externas, ejecutar pruebas reales contra esos servicios vuelve tu suite lenta, frágil y costosa. Aprender a usar mocks en pruebas unitarias de Python te permite simular respuestas externas, aislar tu código y mantener tests rápidos y confiables. Esta guía está pensada para desarrolladores que ya escriben pruebas con unittest y quieren llevar su práctica al siguiente nivel.

¿Qué es un mock y por qué lo necesitas en tus pruebas?

Un mock es un objeto que reemplaza una pieza real de código durante la ejecución de una prueba. En lugar de dejar que Python ejecute la llamada original, le dices: "no ejecutes eso, devuelve este valor en su lugar".

¿Qué es un mock en Python? Es una técnica para sustituir una función o módulo durante una prueba y devolver un valor controlado, evitando llamadas reales a servicios externos.

Esto es clave cuando trabajas con integraciones a APIs, bases de datos o servicios que requieren autenticación. El objetivo es mantener el entorno de pruebas aislado de errores que no dependen de tu código.

¿Cómo construir una función que consuma una API externa?

Imagina que un sistema bancario debe restringir depósitos según el país del usuario. Para resolverlo, necesitas una función que reciba una IP y devuelva la ubicación geográfica asociada [00:42].

La implementación usa la librería requests, que se instala con pip install requests. Después conviene congelar la versión en un archivo requirements.txt ejecutando pip freeze | grep requests para copiar la línea exacta [01:35].

python import requests

def get_location(ip): url = f"https://api.ejemplo.com/{ip}" response = requests.get(url) response.raise_for_status() return response.json()

Al probar la función con la IP de Google 8.8.8.8, el JSON devuelto incluye campos como country_name, region_name y city_name [02:50]. Funciona, pero hace una petición real a Internet en cada ejecución.

¿Por qué tus pruebas tardan tanto sin mocks?

Al correr el test contra la API real, la ejecución toma cerca de medio segundo. Otras pruebas que no salen a Internet, como assertions sobre lógica interna, terminan en cero segundos [04:20]. Esa diferencia se multiplica cuando tu suite crece a cientos de tests.

¿Cómo aplicar @patch de unittest.mock paso a paso?

El módulo unittest.mock incluye el decorador patch, que sobrescribe temporalmente cualquier objeto durante la prueba. La sintaxis es directa:

python from unittest import TestCase from unittest.mock import patch from src.api_client import get_location

class APIClientTests(TestCase): @patch("src.api_client.requests.get") def test_get_location_returns_expected_data(self, mock_get): mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = { "country_name": "USA", "region_name": "Florida", "city_name": "Miami" } result = get_location("8.8.8.8") self.assertEqual(result["country_name"], "USA")

La ruta dentro de @patch debe apuntar al módulo donde se usa la función, no donde está definida. Por eso se escribe src.api_client.requests.get y no simplemente requests.get [05:40].

¿Dónde debo aplicar @patch, en el módulo original o donde se usa? Siempre donde se importa y usa la función. Si tu código hace import requests dentro de api_client.py, el patch va sobre src.api_client.requests.get.

¿Cómo descubrir qué datos devuelve la API real para tu mock?

Para construir un mock fiel, necesitas conocer la estructura del JSON real. Una técnica práctica es usar la librería ipdb, importarla en el código y agregar ipdb.set_trace() justo después de la llamada [07:15]. Eso pausa la ejecución y te permite inspeccionar la variable data directamente desde la terminal.

Una vez que tienes el formato real, copias solo los campos que tu código consume. No necesitas reproducir todo el JSON, basta con las claves que tu función realmente lee.

¿Cómo verificar que tu código llama la URL correcta?

Uno de los riesgos al usar mocks es olvidar validar la URL real. El mock acepta cualquier llamada y devuelve lo configurado, así que un error en la construcción de la URL pasaría desapercibido hasta producción.

La solución es usar assert_called_once_with:

python mock_get.assert_called_once_with("https://api.ejemplo.com/8.8.8.8")

Esta aserción confirma dos cosas: que requests.get se llamó exactamente una vez y que se invocó con la URL esperada [09:30]. Si cambias un dígito de la IP en la prueba, el test falla y te muestra cuál fue la llamada real frente a la esperada.

¿Qué ganas al mockear servicios con autenticación o tokens?

La misma técnica funciona para servidores que exigen tokens, usuario y contraseña, o cualquier credencial. En lugar de exponer secretos en tu suite de pruebas, simulas la respuesta como si la autenticación ya hubiera sido exitosa y validas el comportamiento de tu aplicación con esa data.

Los beneficios concretos al usar mocks correctamente son:

  • Pruebas que pasan de medio segundo a una milésima de segundo.
  • Independencia de la disponibilidad de servicios externos.
  • Cero credenciales expuestas en el repositorio.
  • Capacidad de simular escenarios de error difíciles de reproducir.

Como reto, agrega un parámetro adicional a get_location para devolver el código de país, escribe una nueva prueba que mockee ese campo y valida con assert_called_once_with que la URL incluye los parámetros correctos. Cuéntame en los comentarios qué API estás integrando y cómo resolviste el mock.