Mocking de APIs GraphQL: Pruebas Unitarias y de Integración con Jest y Apollo Client
Este tutorial práctico te enseña cómo configurar y ejecutar pruebas automatizadas en aplicaciones que consumen APIs GraphQL. Descubrirás técnicas avanzadas de mocking utilizando Jest y herramientas nativas de Apollo Client para asegurar la robustez de tu código frontend sin depender de un servidor real.
En el desarrollo de aplicaciones web modernas que consumen APIs GraphQL, enfrentarse a la dependencia de un servidor backend en funcionamiento puede ralentizar el ciclo de desarrollo y dificultar la ejecución de pruebas automatizadas. El mocking de datos (simulación de respuestas) es una técnica indispensable para aislar nuestros componentes de interfaz de usuario y asegurar que nuestra lógica de negocio funcione bajo cualquier escenario. Intermedio
🎯 ¿Qué es el Mocking en GraphQL y por qué lo necesitas?
A diferencia de REST, donde cada endpoint es una URL independiente que se puede interceptar fácilmente con herramientas como nock o MSW a nivel de red, GraphQL utiliza una única URL POST para todas las operaciones (consultas, mutaciones y suscripciones). Esto significa que interceptar peticiones basándose únicamente en la URL ya no es suficiente.
El mocking en GraphQL opera a nivel de esquema o a nivel de red mediante la interceptación de documentos GraphQL (las consultas enviadas). Existen dos enfoques principales:
- Mocking a nivel de esquema (Schema Mocking): Ideal para el desarrollo del backend o para pruebas de integración del servidor, donde generamos datos falsos automáticamente basados en los tipos definidos en el esquema SDL.
- Mocking a nivel de cliente (Client Mocking): Esencial para los desarrolladores frontend. Consiste en simular las respuestas que devolvería el servidor cuando el cliente (como Apollo Client) ejecuta una consulta específica.
🛠️ Configuración del Entorno de Pruebas
Para este tutorial, utilizaremos un entorno basado en Node.js, React, Apollo Client y Jest junto con React Testing Library. Si aún no tienes un proyecto configurado, puedes inicializar uno o seguir los siguientes pasos en tu proyecto existente.
Instalación de Dependencias
Asegúrate de tener instaladas las siguientes dependencias de desarrollo y producción en tu proyecto:
npm install @apollo/client graphql npm install --save-dev jest @testing-library/react @testing-library/jest-dom
📊 Mocking en el Cliente con Apollo MockedProvider
El componente MockedProvider de Apollo Client permite envolver nuestros componentes de React durante las pruebas y proporcionarles un conjunto predefinido de pares de solicitud-respuesta (request-response).
Estructura de un Mock con Apollo Client
Veamos un ejemplo práctico de un componente que muestra una lista de usuarios y cómo escribir su prueba unitaria.
Componente a Probar: UserProfile.jsx
import React from 'react'; import { useQuery, gql } from '@apollo/client';
export const GET_USER_QUERY = gql query GetUser($id: ID!) { user(id: $id) { id name email } };
export function UserProfile({ userId }) { const { loading, error, data } = useQuery(GET_USER_QUERY, { variables: { id: userId }, });
if (loading) return
Cargando...
; if (error) returnError al cargar el usuario.
;return (
{data.user.name}
Email: {data.user.email}
Escribiendo la Prueba con Jest y MockedProvider
Ahora crearemos el archivo de prueba UserProfile.test.jsx. Aquí definiremos los mocks exactos que coincidan con la consulta y las variables esperadas.
import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import { MockedProvider } from '@apollo/client/testing'; import { UserProfile, GET_USER_QUERY } from './UserProfile';
const mocks = [ { request: { query: GET_USER_QUERY, variables: { id: '1' }, }, result: { data: { user: { id: '1', name: 'Ana Pérez', email: 'ana.perez@example.com', }, }, }, }, ];
describe('Componente UserProfile', () => {
it('debe renderizar los datos del usuario correctamente', async () => {
render(
// Verificar estado de carga inicial
expect(screen.getByText(/cargando.../i)).toBeInTheDocument();
// Esperar a que aparezcan los datos simulados
expect(await screen.findByText('Ana Pérez')).toBeInTheDocument();
expect(screen.getByText('Email: ana.perez@example.com')).toBeInTheDocument();
}); });
⚠️ Manejo de Errores y Casos Límite en las Pruebas
No todas las peticiones a la API son exitosas. Es fundamental probar cómo reacciona nuestra interfaz de usuario cuando el servidor devuelve un error de GraphQL o un error de red.
Simulando un Error de GraphQL
Para simular un error devuelto por el servidor GraphQL, podemos incluir la propiedad error en el objeto mock en lugar de result.
const errorMock = [ { request: { query: GET_USER_QUERY, variables: { id: '999' }, }, error: new Error('Usuario no encontrado'), }, ];
it('debe mostrar un mensaje de error cuando falla la consulta', async () => {
render(
expect(await screen.findByText(/error al cargar el usuario/i)).toBeInTheDocument(); });
🔄 Mocking Dinámico y Respuestas Secuenciales
En ocasiones, un componente realiza la misma consulta varias veces o necesitamos simular un comportamiento donde la primera llamada falla y la segunda es exitosa. MockedProvider permite manejar respuestas secuenciales utilizando la propiedad delay o múltiples entradas en el array de mocks.
Ver ejemplo de mutaciones simuladas
Para simular mutaciones (operaciones de escritura), el proceso es idéntico al de las consultas. Debes proporcionar la mutación con sus variables de entrada y el resultado esperado que simule la actualización de datos en el servidor:const mutationMock = [
{
request: {
query: UPDATE_USER_MUTATION,
variables: { id: '1', name: 'Nuevo Nombre' },
},
result: {
data: {
updateUser: {
id: '1',
name: 'Nuevo Nombre',
},
},
},
},
];
🚀 Buenas Prácticas para el Mocking en GraphQL
Para mantener tus pruebas limpias, mantenibles y libres de falsos positivos, ten en cuenta las siguientes recomendaciones:
- Reutiliza tus Mocks: Crea archivos de datos simulados centralizados (por ejemplo,
userMocks.js) para compartirlos entre diferentes archivos de prueba. - Cuidado con
addTypename: Por defecto, Apollo Client añade la propiedad__typenamea los objetos devueltos. Si tus mocks no la incluyen, asegúrate de configuraraddTypename={false}en elMockedProvidero incluir los campos__typenameen tus datos simulados. - Prueba estados de carga: Valida siempre que los indicadores de carga (
loading: true) se renderizan correctamente antes de que aparezcan los datos.
| Estrategia | Ventajas | Desventajas | Cuándo Usar |
|---|---|---|---|
| --- | --- | --- | --- |
| MockedProvider (Cliente) | Nativo de Apollo, fácil de configurar, excelente para UI | Requiere definir mocks estrictos por cada consulta | Pruebas unitarias y de componentes React |
| MSW (Mock Service Worker) | Intercepta a nivel de red, reutilizable con REST | Configuración inicial más compleja | Pruebas de integración E2E o componentes complejos |
| --- | --- | --- | --- |
| Schema Mocking (Servidor) | Genera datos automáticos masivos | Menos control sobre casos de borde específicos | Pruebas de integración del backend |
📌 Conclusión
El mocking de APIs GraphQL es una habilidad esencial que transforma la manera en que desarrollamos y testeamos aplicaciones web modernas. Utilizando herramientas como Jest y el MockedProvider de Apollo Client, puedes construir suites de pruebas robustas, rápidas y totalmente independientes de la infraestructura del backend. Implementa estas técnicas en tu flujo de trabajo diario y garantiza la calidad y estabilidad de tus interfaces de usuario.
Tutoriales relacionados
- Diseñando APIs GraphQL Robustas con Directivas Personalizadas: Más Allá de lo Básicointermediate20 min
- Optimización de GraphQL con Dataloader: Estrategias para Evitar el Problema N+1intermediate15 min
- Suscribiendo Datos en Tiempo Real con GraphQL: Implementando Subscriptions para Experiencias Dinámicasintermediate15 min
- Optimización de Consultas GraphQL: Estrategias para APIs Más Rápidas y Eficientesintermediate12 min
- Explorando la Integración de Cargas de Archivos en GraphQL: Una Guía Prácticaintermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!