Cómo probar componentes en React con Jest y React Testing Library: Guía paso a paso con el registro de usuarios de Duolingo

Publicado: Hace 10 días

¿Por qué probar tus componentes en React es importante?

Las aplicaciones frontend son cada vez más dinámicas: múltiples componentes que se comunican entre sí, cada uno con una responsabilidad clara… al menos en teoría. En la práctica, las prisas y un mal diseño pueden convertir el desarrollo en un proceso frustrante, especialmente si no realizamos pruebas en nuestros componentes.


Probar tus componentes es la forma de asegurar que hacen exactamente lo que deberían hacer. Por ejemplo, en un registro de usuario, validar que la información enviada al backend tenga el formato correcto.


Además, las pruebas ofrecen beneficios clave:


  • Funcionan como documentación viva para el desarrollador.
  • Son fáciles y rápidas de ejecutar.
  • No requieren configuración de servicios externos.
  • Dan confianza para agregar o modificar código.
  • Permiten refactorizar sin miedo a romper algo.

Si quieres profundizar más en el tema, visita el siguiente enlace: Deja de probar todo manualmente: empieza a confiar en tus pruebas automatizadas

Contexto: El flujo de registro de usuarios en Duolingo

A simple vista, el flujo de registro de usuarios en Duolingo parece sencillo. Sin embargo, por dentro hay todo un conjunto de servicios trabajando en conjunto para que puedas crear una cuenta y empezar a aprender un idioma.


Ahora bien, ¿y si pudiéramos evitar configurar todos estos servicios y enfocarnos directamente en la funcionalidad principal? 


A qué me refiero con esto, en una arquitectura bien diseñada, cada servicio tiene (en teoría) una única responsabilidad. Por ejemplo:


  • Un API de usuarios para gestionar todo lo relacionado con cuentas.
  • Un API de suscripciones para manejar planes y pagos.

El frontend, por su parte, tiene la misión de conectar al usuario con estos servicios mediante contratos claros con el backend, donde se especifica el formato exacto de la información que se enviará.

Como desarrolladores frontend, no necesitamos conocer en detalle el funcionamiento interno de cada servicio (aunque entenderlo siempre suma). Lo esencial es asegurarnos de:


  • Enviar la data tal como lo indica el contrato.
  • Gestionar los errores de manera adecuada.
  • Guiar al usuario para que logre su objetivo.

En resumen, nuestro papel es ser el puente entre el usuario y los servicios.


Flujo principal del registro

En el flujo principal, Duolingo pide cuatro datos: edad, nombre, correo y contraseña.

Todos son obligatorios, excepto el nombre.


Cada uno tiene su propio formato:


  • Edad. debe ser un número
  • Nombre. Una cadena de texto
  • Correo. Texto con formato válido de email
  • Contraseña. Al menos 6 caracteres

Duolingo. Flujo principal

Flujos alternos

Además del flujo principal, Duolingo maneja algunos casos especiales que debemos contemplar en las pruebas:


  • Usuarios menores de 13 años. En este caso se solicitan los mismos campos que en el flujo principal, pero con un cambio importante: los placeholders de nombre y correo se actualizan para indicar que no se ingrese el nombre real y que se utilice el correo electrónico de un familiar
  • Inicio de sesión con terceros. El usuario puede registrarse o acceder usando su cuenta de Google o Facebook.

Duolingo: Flujos alternos

Validaciones

Tanto el flujo principal como los alternos comparten los mismos campos, y cada uno debe cumplir con un formato válido. Si alguna validación falla, se muestra un mensaje de error al usuario.


Validaciones principales:


  • Correo. Debe tener un formato válido de correo.
  • Contraseña. Debe cumplir con el formato requerido (mínimo 6 caracteres).
  • Correo ya registrado. Si el email existe en la base de datos, se muestra un mensaje de que la cuenta ya está registrada.

Duolingo. Validaciones


Implementando pruebas paso a paso

Sin importar si usas React, Angular o Vue (o incluso backend), vas a necesitar un set de herramientas para escribir y ejecutar pruebas. 


En el frontend, lo mínimo es:


  • Framework de pruebas.
  • Un entorno de navegador para tests.
  • Utilidades para probar la UI.

En este artículo usaremos Jest como framework principal: te da el runner, las aserciones y los mocks en una sola herramienta, y funciona perfecto con React. Lo complementamos con React Testing Library para probar la interfaz desde la perspectiva del usuario (seleccionar por roles, texto, labels, etc.) 


Resumen:

  • Jest. Ejecuta las pruebas, aserciones y mocks.
  • Jsdom. Simula el DOM en Node.
  • Testing-library/react. Se encarga de renderizar nuestros componentes y ofrecer queries para acceder a los elementos de la interfaz, tal como lo haría un usuario (por texto, rol, etiqueta, placeholder, etc.).


Con esto listo, pasamos a cubrir el flujo principal, los flujos alternos y las validaciones del registro de Duolingo, siempre desde el comportamiento del usuario.

Configuración del entorno de pruebas

Para enfocarnos directamente en nuestro objetivo —probar nuestros componentes en React— vamos a evitar configurar el entorno desde cero. En su lugar, usaremos Create React App, que ya trae todo listo para ejecutar pruebas con Jest y jsdom.


npx create-react-app duolingo
cd duolingo
npm start

De esta forma, podremos comenzar a escribir pruebas sin preocuparnos por la configuración inicial.


NOTA: No se recomienda usar Create React App para aplicaciones en producción. Para proyectos reales, es mejor optar por herramientas más modernas y rápidas como Vite, Parcel o similares.

Implementación del flujo principal

NOTA: Cuando empezamos a trabajar con pruebas, lo habitual es escribir primero la prueba en lugar de implementar directamente la solución en el componente principal.

Definición de la prueba

    1: /**
    2:  * @jest-environment jsdom
    3:  */
    4: import React from 'react'
    5: import DuolingoRegistro from "./DuolingoRegistro";
    6: import {fireEvent, render, screen} from "@testing-library/react";
    7: 
    8: describe('DuolingoRegistro', () => {
    9:   it('crear cuenta', () => {
   10:     const props = { crearUsuarioApi: jest.fn()};
   11:     render(<DuolingoRegistro {… props } />)
   12: 
   13:     fireEvent.change(screen.getByTestId('edad'), { target: { value: 25 }})
   14:     fireEvent.change(screen.getByTestId('nombre'), { target: { value: "Gabriel" }})
   15:     fireEvent.change(screen.getByTestId('correo'), { target: { value: "cualquiercorreo" }})
   16:     fireEvent.change(screen.getByTestId('contrasena'), { target: { value: "cualquiercontraseña" }})
   17: 
   18:     fireEvent.submit(screen.getByText("Guardar"))
   19: 
   20:     expect(props.crearUsuarioApi).toBeCalledWith({
   21:       edad: "25",
   22:       nombre: "Gabriel",
   23:       correo: "cualquiercorreo",
   24:       contrasena: "cualquiercontraseña",
   25:     })
   26:   })
   27: })


Línea 1–3
Indicamos que las pruebas se ejecutarán en un entorno que simula un navegador usando jsdom.


Línea 8
Agrupamos las pruebas relacionadas con el componente DuolingoRegistro mediante describe.


Línea 9
Definimos nuestra primera prueba con el nombre “crear cuenta”.


Línea 10
Creamos un prop que simula la función del API para la creación de un usuario.


Línea 11
Renderizamos el componente dentro del navegador simulado y le pasamos los props definidos anteriormente.


Línea 13–16
Simulamos la escritura en los campos edad, nombre, correo y contraseña. Para seleccionarlos usamos el objeto screen y para interactuar con ellos utilizamos fireEvent.


Línea 18
Simulamos el envío de la información haciendo clic en el botón Guardar.


Línea 20
Verificamos que la función del API (crearUsuarioApi) haya sido llamada con los datos ingresados por el usuario.


NOTA: En Jest, la función expect se utiliza para crear una afirmación sobre el valor que queremos comprobar en la prueba. Por sí sola no valida nada, simplemente prepara el valor recibido para compararlo con un matcher.


Los matchers son los métodos que definen la condición que debe cumplirse. Por ejemplo:


  • toBe() compara igualdad estricta.
  • toEqual() compara igualdad profunda en objetos/arrays.
  • toBeCalledWith() verifica que una función simulada haya sido llamada con ciertos argumentos.

En conjunto, expect y los matchers nos permiten expresar de forma clara qué resultado esperamos de nuestro código.

Solución de la prueba

    1: import React, {useState} from 'react'
    2: 
    3: const DuolingoRegistro = ({ crearUsuarioApi }) => {
    4:   const [values, setValues] = useState({
    5:     edad: '',
    6:     nombre: '',
    7:     correo: '',
    8:     contrasena: '',
    9:   });
   10: 
   11:   function handleSubmit() {
   12:     crearUsuarioApi(values)
   13:   }
   14: 
   15:   function handleChange(e) {
   16:     e.preventDefault()
   17:     const { name, value } = e.target;
   18:     setValues({...values, [name]: value});
   19:   }
   20: 
   21:   return(
   22:     <form onSubmit={handleSubmit}>
   23:       <input type="edad" name='edad' value={values.edad} data-testid='edad' onChange={handleChange} />
   24:       <input type="nombre" name='nombre' value={values.nombre} data-testid='nombre' onChange={handleChange} />
   25:       <input type="correo" name='correo' value={values.correo} data-testid='correo' onChange={handleChange} />
   26:       <input type="contrasena" name='contrasena' value={values.contrasena} data-testid='contrasena' onChange={handleChange} />
   27:       <button type="submit">Guardar</button>
   28:     </form>
   29:   )
   30: }
   31: 
   32: export default DuolingoRegistro;


Líneas 4–9
Definimos el estado values con useState para almacenar los valores que el usuario ingresa en los campos del formulario.


Líneas 11–13
Función handleSubmit: se ejecuta al enviar el formulario y llama a crearUsuarioApi pasando los datos almacenados en values.


Líneas 15–19
Función handleChange: actualiza el estado values cada vez que el usuario escribe en un campo del formulario, manteniendo sincronizada la interfaz con el estado interno.


Líneas 21–29
Renderizamos el formulario con los campos edad, nombre, correo y contraseña. Cada campo incluye atributos como type, name, value y data-testid (este último usado para las pruebas automatizadas). El formulario finaliza con un botón Guardar que dispara el envío de la información.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'crear cuenta'
 PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ crear cuenta (35 ms)

Test Suites: 1 passed, 1 total
Tests:       0 skipped, 1 passed, 1 total
Snapshots:   0 total
Time:        0.598 s, estimated 1 s
Ran all test suites matching /src\/DuolingoRegistro\/DuolingoRegistro.test.js/i with tests matching "crear cuenta”.

Implementación de validaciones

Validación: correo con formato inválido


Definición de la prueba

. . .
    28:   it('mostrar un error cuando el correo tenga un formato inválido', () => {
   29:     render(<DuolingoRegistro />)
   30: 
   31:     fireEvent.change(screen.getByTestId('correo'), { target: { value: "cualquiercorreo" }})
   32: 
   33:     fireEvent.submit(screen.getByText(‘Guardar'))
   34: 
   35:     expect(document.body.textContent).toContain('El correo tiene un formato inválido')
   36:   })
. . .


Línea 28

Definimos la prueba con el nombre “mostrar un error cuando el correo tenga un formato inválido”


Línea 29

Montamos el componente.


Línea 31

Escribimos un correo con un formato inválido.


Línea 33

Enviamos la data al backend haciendo clic en el botón Guardar.


Línea 35

Validamos que el mensaje de error “El correo tiene un formato inválido” se muestre en el navegador simulado.

Solución de la prueba

. . .
    3: const DuolingoRegistro = ({ crearUsuarioApi }) => {
. . .
   10:   const [errors, setErrors] = useState([]);
   11: 
   12:   function esCorreoValido(correo) {
   13:     const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
   14:     return regex.test(correo);
   15:   }
. . .
   21:   function handleSubmit() {
   22:     const _errors = []
   23:     if(!esCorreoValido(values.correo)) {
   24:       _errors.push('El correo tiene un formato inválido')
   25:     }
   30: 
   31:     if(_errors.length > 0) {
   32:       setErrors(_errors)
   33:       return
   34:     }
   35: 
   36:     crearUsuarioApi(values)
   37:   }
   38: 
   39:   return(
   40:     <form onSubmit={handleSubmit}>
   41:       <input type="edad" name='edad' value={values.edad} data-testid='edad' onChange={handleChange} />
   42:       <input type="nombre" name='nombre' value={values.nombre} data-testid='nombre' onChange={handleChange} />
   43:       <input type="correo" name='correo' value={values.correo} data-testid='correo' onChange={handleChange} />
   44:       <input type="contrasena" name='contrasena' value={values.contrasena} data-testid='contrasena' onChange={handleChange} />
   45:       <button type="submit">Guardar</button>
   46:       <div>
   47:         {errors.length > 0 && errors.map(error => error)}
   48:       </div>
   49:     </form>
   50:   )
. . .


Línea 10
Definimos el estado errors para almacenar los mensajes de validación que puedan generarse.


Líneas 12–15
Creamos esCorreoValido(correo), una función que valida el formato del correo mediante una expresión regular.


Líneas 21–37
En handleSubmit validamos el formulario antes de enviar los datos: si el correo no tiene un formato válido, agregamos un mensaje a _errors y detenemos el envío; si no hay errores, llamamos a crearUsuarioApi(values).


Línea 47
Mostramos los mensajes de error cuando existan errores.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'mostrar un error cuando el correo tenga un formato inválido'
 PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ mostrar un error cuando el correo tenga un formato inválido (35 ms)
    ○ skipped crear cuenta

Test Suites: 1 passed, 1 total
Tests:       1 skipped, 1 passed, 2 total
. . .

Validación: contraseña con formato inválido

Esta prueba es muy similar a la anterior, pero en este caso, hay que validar que la contraseña tenga un formato valido antes de enviarse al backend.


Definición de la prueba

. . .
   38:   it('mostrar un error cuando la contraseña tenga un formato inválido', () => {
   39:     render(<DuolingoRegistro />)
   40: 
   41:     fireEvent.change(screen.getByTestId('contrasena'), { target: { value: "1234" }})
   42: 
   43:     fireEvent.submit(screen.getByText(‘Guardar'))
   44: 
   45:     expect(document.body.textContent).toContain('La contraseña es muy corta')
   46:   })
. . .


Línea 41

Escribimos una contraseña con un formato inválido


Línea 45

Validamos que el error “La contraseña es muy corta” exista cuando se escriba una contraseña con formato inválido.

Solución de la prueba

. . .
    3: const DuolingoRegistro = ({ crearUsuarioApi }) => {
. . .
   17:   function esContrasenaValida(contrasena) {
   18:     return contrasena.length >= 6;
   19:   }
   20: 
   21:   function handleSubmit() {
   22:     const _errors = []
. . .
   26: 
   27:     if(!esContrasenaValida(values.contrasena)) {
   28:       _errors.push('La contraseña es muy corta')
   29:     }
   30: 
   31:     if(_errors.length > 0) {
   32:       setErrors(_errors)
   33:       return
   34:     }
   35: 
   36:     crearUsuarioApi(values)
   37:   }
. . .


Líneas 17–19
Definimos esContrasenaValida(contrasena), una función que verifica si la contraseña cumple con la longitud mínima (≥ 6 caracteres).


Líneas 27–29
Si la contraseña no es válida, agregamos el mensaje de error correspondiente para mostrárselo al usuario.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'mostrar un error cuando la contraseña tenga un formato inválido'
 PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ mostrar un error cuando la contraseña tenga un formato inválido (35 ms)
    ○ skipped crear cuenta
    ○ skipped mostrar un error cuando el correo tenga un formato inválido

Test Suites: 1 passed, 1 total
Tests:       2 skipped, 1 passed, 3 total
. . .

Validación: Si un correo ya se encuentra registrado

Esta prueba es muy similar a las dos últimos. Sin embargo, aquí la validación si un correo existe viene directamente desde el backend.

Definición de la prueba

. . .
   48:   it('mostrar un error cuando el correo ya exista', async () => {
   49:     const props = {validarCorreoApi: jest.fn().mockRejectedValue({})}
   50:     render(<DuolingoRegistro {…props} />)
   51: 
   52:     fireEvent.blur(screen.getByTestId('correo'), { target: { value: "[email protected]" }})
   53: 
   54:     expect(props.validarCorreoApi).toBeCalledWith(‘[email protected]')
   55: 
   56:     await waitFor(() => {
   57:       expect(document.body.textContent).toContain('Correo ya existe')
   58:     })
   59:   })
. . .


Línea 48
Definimos la prueba “mostrar un error cuando el correo ya exista”.


Línea 49
Creamos un mock de validarCorreoApi que rechaza la promesa para simular que el correo ya está registrado.


Línea 50
Renderizamos el componente y le pasamos los props definidos.


Línea 52
Escribimos el correo en el input y disparamos el evento blur para activar la validación.


Línea 54
Verificamos que validarCorreoApi haya sido llamada con el correo ingresado.


Líneas 56–58
Esperamos a que aparezca en pantalla el mensaje de error “Correo ya existe” y confirmamos que se muestre al usuario.

Solución de la prueba

. . .
    3: const DuolingoRegistro = ({ crearUsuarioApi, validarUsuarioApi }) => {
. . .
   45:   async function handleCorreoChange(e) {
   46:     const { value } = e.target;
   47: 
   48:     try {
   49:       await validarUsuarioApi(value)
   50:     } catch (err) {
   51:       setErrors([...errors, 'Correo ya existe']);
   52:     }
   53:   }
   54: 
   55:   return(
   56:     <form onSubmit={handleSubmit}>
. . .
   59:       <input type="correo" name='correo' value={values.correo} data-testid='correo' onChange={handleChange} onBlur={handleCorreoChange} />
. . .


Línea 45-53

Definimos handleCorreoChange: al perder el foco del campo correo, llama a validarUsuarioApi(value). Si la promesa se rechaza, añadimos el mensaje de error “Correo ya existe” al estado.


Línea 59
Asociamos handleCorreoChange al evento onBlur del input de correo; la validación se ejecuta cuando el usuario deja de escribir en el campo.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'mostrar un error cuando el correo ya exista'                    
 PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ mostrar un error cuando el correo ya exista (35 ms)
    ○ skipped crear cuenta
    ○ skipped mostrar un error cuando el correo tenga un formato inválido
    ○ skipped mostrar un error cuando la contraseña tenga un formato inválido

Test Suites: 1 passed, 1 total
Tests:       3 skipped, 1 passed, 4 total
. . .

Implementación de flujos alternos

Flujos alternos: menor a 13 años


Definición de la prueba

. . .
   61:   it('usuario con edad menor a 13 años', () => {
   62:     render(<DuolingoRegistro />)
   63: 
   64:     fireEvent.change(screen.getByTestId('edad'), { target: { value: 12 }})
   65: 
   66:     const nombre = screen.getByTestId('nombre');
   67:     const correo = screen.getByTestId('correo');
   68: 
   69:     expect(nombre.placeholder).toBe('Nombre del tutor');
   70:     expect(correo.placeholder).toBe('Correo del tutor');
   71:   });
. . .


Línea 62
Renderizamos el componente bajo prueba.


Línea 64
Simulamos que el usuario ingresa una edad menor a 13 (12) para activar la lógica de placeholders.


Líneas 66–70
Obtenemos los inputs de nombre y correo y verificamos que sus placeholder cambien a “Nombre del tutor” y “Correo del tutor”, respectivamente.

Solución de la prueba

. . .
    3: const DuolingoRegistro = ({ crearUsuarioApi, validarUsuarioApi }) => {
 . . .
   55:   const esMenor = Number(values.edad) < 13
   56: 
   57:   return(
   58:     <form onSubmit={handleSubmit}>
. . .
   60:       <input type="nombre" name='nombre' value={values.nombre} data-testid='nombre' onChange={handleChange}
   61:              placeholder={esMenor ? 'Nombre del tutor' : 'Tu nombre'}
   62:       />
   63:       <input type="correo" name='correo' value={values.correo} data-testid='correo' onChange={handleChange} onBlur={handleCorreoChange}
   64:              placeholder={esMenor ? 'Correo del tutor' : 'Tu correo'}
   65:       />
. . .


Línea 55
Determinamos si la edad es menor a 13 años.

Línea 61-64
Si la edad es menor a 13 años, modificamos los placeholders de los campo nombre y correo.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'usuario con edad menor a 13 años'
PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ usuario con edad menor a 13 años (33 ms)
    ○ skipped crear cuenta
    ○ skipped mostrar un error cuando el correo tenga un formato inválido
    ○ skipped mostrar un error cuando la contraseña tenga un formato inválido
    ○ skipped mostrar un error cuando el correo ya exista

Test Suites: 1 passed, 1 total
Tests:       4 skipped, 1 passed, 5 total
. . .

Flujos alternos: inicio de sesión con terceros


Definición de la prueba

. . .
   73:   it('inicio de sesión usando Google', async () => {
   74:     const props = {inicioSesionGoogleApi: jest.fn().mockResolvedValue({})};
   75:     render(<DuolingoRegistro { ...props} />)
   76: 
   77:     fireEvent.click(screen.getByText("Google"))
   78: 
   79:     await waitFor(() => {
   80:       expect(document.body.textContent).toContain('Se ha iniciado correctamente usando Google')
   81:     })
   82:   })
. . .


Línea 74
Mockeamos inicioSesionGoogleApi para que resuelva exitosamente y así simular un inicio de sesión correcto con Google.

Línea 77
Simulamos el clic en el botón Google para iniciar el flujo de autenticación.

Línea 79–81
Esperamos la respuesta del backend y verificamos que se muestre la notificación de éxito “Se ha iniciado correctamente usando Google”.

Solución de la prueba

. . .
    3: const DuolingoRegistro = ({ crearUsuarioApi, validarUsuarioApi, inicioSesionGoogleApi }) => {
. . .
   11:   const [notification, setNotification] = useState(null);
. . .
   56:   async function handleGoogle(e) {
   57:     try {
   58:       await inicioSesionGoogleApi()
   59:       setNotification('Se ha iniciado correctamente usando Google')
   60:     } catch (err) {
   61:     }
   62:   }
. . .
   66:   return(
   67:     <form onSubmit={handleSubmit}>
   68:       <div>
   69:         {notification && notification}
   70:       </div>
. . .
   80:       <div>
   81:         <button type='button' onClick={handleGoogle}>Google</button>
   82:       </div>
 . . .


Línea 11
Definimos el estado local notification para mostrar mensajes al usuario (p. ej., confirmaciones de éxito).

Líneas 56–61
handleGoogle: llama a inicioSesionGoogleApi() y, si resuelve correctamente, establece el mensaje “Se ha iniciado correctamente usando Google”. (En caso de error, debería gestionarse para informar al usuario).

Líneas 68–69
Mostramos la notificación cuando existe un valor en notification.

Líneas 80–82
Botón que dispara el flujo de inicio de sesión con Google mediante handleGoogle.

Ejecución de la prueba

➜  testing git:(main) ✗ jest src/DuolingoRegistro/DuolingoRegistro.test.js --t 'inicio de sesión usando Google'
 PASS  src/DuolingoRegistro/DuolingoRegistro.test.js
  DuolingoRegistro
    ✓ inicio de sesión usando Google (36 ms)
    ○ skipped crear cuenta
    ○ skipped mostrar un error cuando el correo tenga un formato inválido
    ○ skipped mostrar un error cuando la contraseña tenga un formato inválido
    ○ skipped mostrar un error cuando el correo ya exista
    ○ skipped usuario con edad menor a 13 años

Test Suites: 1 passed, 1 total
Tests:       5 skipped, 1 passed, 6 total
. . .


NOTA: El inicio de sesión con Facebook se implementa igual que con Google. Repite el patrón (API → botón → notificación → pruebas) y práctica.

Conclusiones

Tomando como ejemplo el flujo de registro de usuarios de Duolingo, aprendimos varias técnicas clave de pruebas en frontend:

  • Requisitos primero. Es esencial definir claramente los requisitos desde el inicio; sin ellos, es difícil que la aplicación haga lo que debe.
  • Ciclo TDD. Trabajamos con el patrón Test-Driven Development: escribir la prueba, implementar lo mínimo para pasarla, ejecutar/refactorizar.
  • Herramientas. Usamos un framework de testing (Jest + React Testing Library) y un entorno de navegador simulado (jsdom) para probar el componente.
  • Aserciones. Entendimos qué es expect y cómo los matchers expresan el comportamiento esperado del código bajo prueba.


Con esta base, podemos iterar con confianza: las pruebas guían la implementación, reducen regresiones y mejoran la calidad del código.

¿Quieres profundizar más en pruebas con React?

En mi libro Testing en React: Guía práctica con Jest y React Testing Library encontrarás ejemplos prácticos, técnicas avanzadas y estrategias para escribir pruebas efectivas.