Contenido del curso
Estrategias de Testing en React
Testing de Flujos de Usuario en React
Testing de Hooks en React
Pruebas de Integración y APIs en React
Reflexiones sobre Testing en React
Pruebas de Error en Login con React y Vitest
Resumen
Probar la pantalla de login en React es uno de los casos más críticos cuando construyes un sistema con autenticación, porque ahí se decide si un usuario entra o no. Aquí vas a ver cómo escribir un test que valida un mensaje de error con credenciales inválidas usando Vitest, Testing Library y mocks de dependencias externas.
Por qué la pantalla de login necesita pruebas automatizadas
El login es la puerta de entrada de tu aplicación, y cualquier fallo silencioso ahí bloquea a todos tus usuarios. En Platzi Orders, el componente de login vive en src/containers/login y maneja inputs de username y password, un botón de submit y un mensaje de error que se alimenta de un state de React.
Ese estado se actualiza cuando la promesa de inicio de sesión cae en su rama fallida. Y justo ahí está el reto: en un ambiente de pruebas necesitas simular esa falla sin depender de un backend real, y para eso entran los mocks [01:00].
¿Qué es un mock en testing? Es un doble de acción de una función real. Reemplaza a la función original durante el test para que puedas controlar qué retorna, sin ejecutar la lógica verdadera ni llamar APIs.
Cómo configurar el archivo de test del componente login
El primer paso es crear login.test.tsx dentro de la misma carpeta del componente. Desde ahí importas describe, it y expect de Vitest, y render de Testing Library para montar el componente en el entorno de pruebas.
Al correr yarn test por primera vez, lo más probable es que veas errores. No te asustes: los errores son los que te dan seniority. Cada uno te está diciendo qué dependencia falta envolver.
Qué hace MemoryRouter y por qué no usar BrowserRouter
El primer error suele ser que useNavigate no se encuentra. Esto pasa porque el componente usa el hook de react-router-dom, que en producción está envuelto por BrowserRouter desde main.tsx [02:30].
En testing no tienes navegador. Por eso react-router-dom ofrece MemoryRouter, una utilidad pensada para correr en memoria, sin DOM real. Envuelves tu componente con MemoryRouter y el hook deja de quejarse.
Cómo envolver el componente con SessionProvider
El segundo error pide un SessionProvider, que viene de context/AuthContext y es el que expone useSession. Como es un contexto propio del proyecto, no existe un mock automático: tienes que importarlo y usarlo tal cual para envolver el render.
La jerarquía en el test queda así:
MemoryRouterpor fuera para resolver rutas en memoria.SessionProvideradentro para exponer la sesión al componente.<Login />como hijo final.
Cómo mockear getAuth para simular credenciales inválidas
La función que dispara el login es handleSubmit, que llama a handleLogin, que a su vez ejecuta getAuth desde services [05:10]. getAuth retorna una promesa: si todo va bien resuelve, y si no, lanza un new Error con el mensaje de la API.
Para forzar la rama fallida necesitas dos mocks: uno del módulo y otro de la función.
Cómo crear el mock del módulo con vi.mock
Desde Vitest importas vi y el tipo Mock. Luego escribes vi.mock apuntando a la ruta exacta del archivo services/getAuth, y dentro del callback retornas un objeto que reemplaza la exportación real:
ts vi.mock('../../services/getAuth', () => ({ getAuth: vi.fn(), }));
Con vi.fn() ya tienes una función espía vacía, lista para que le digas qué hacer en cada test.
Cómo forzar el rechazo con mockRejectedValue
Después creas una constante mockGetAuth tipada como Mock que apunta al getAuth importado. Esa constante es tu doble de riesgo. Dentro del test le dices que rechace con un error idéntico al real:
ts mockGetAuth.mockRejectedValue(new Error('invalid credentials'));
La regla de oro: el doble tiene que parecerse al actor. Si el original lanza new Error, el mock también lanza new Error, con el mismo texto que esperas validar.
¿Cuándo uso mockRejectedValue? Cuando quieres que una promesa caiga en
.catchotry/catch. Sirve para probar mensajes de error, fallbacks y estados de fallo en la UI.
Cómo simular el llenado de inputs y el clic con fireEvent
Con los mocks listos, toca actuar como un usuario real. Aquí aplica el patrón arrange, act, assert: preparar, ejecutar y verificar.
Para seleccionar los elementos usas distintas queries de Testing Library, y cada una tiene su mejor caso:
getByPlaceholderText('username')para el input de usuario, porque un input no tiene texto pero sí placeholder.getByPlaceholderText('password')para la contraseña, siguiendo la misma lógica.getByRole('button', { name: 'login' })para el botón, filtrando por nombre porque hay más de un botón en pantalla.
Un consejo práctico: copia los valores literales del componente. Un typo te puede costar horas de debugging. Como dice el dicho del tutorial, toma mi consejo y llegarás a viejo.
Cómo disparar onChange y click dentro de act
Los eventos de usuario se envuelven en act de Testing Library para que React procese las actualizaciones de estado antes de la aserción. Como hay promesas, marcas la función como async y usas await:
ts await act(async () => { fireEvent.change(usernameInput, { target: { value: 'wrongUser' } }); fireEvent.change(passwordInput, { target: { value: 'wrongPassword' } }); fireEvent.click(buttonLogin); });
Fuera del act recuperas el mensaje con screen.getByText('invalid credentials') y haces la aserción expect(errorMessage).toBeInTheDocument().
Cómo validar que el test realmente está probando algo
Un test que siempre pasa no sirve. Para confirmar que tu prueba funciona, cambia el texto esperado por algo distinto, como invalidates, y corre de nuevo. Debe fallar, porque el mock está enviando invalid credentials. Si rompe cuando esperas que rompa, tu test es honesto.
Con esto ya tienes cubierto el flujo negativo del login en React, con mocks de módulos externos, providers de testing y simulación de eventos. ¿Qué otro caso de uso del login te gustaría blindar primero, el éxito o la validación de campos vacíos? Cuéntalo en los comentarios.