Gabriel Jiménez | Hace alrededor de 24 horas
Los props son una parte esencial de los componentes en React, validar su comportamiento debería ser obligatorio en nuestros desarrollos. Sin embargo, en muchos de los casos desconocemos cómo hacerlo.
En este artículo, aprenderemos los diferentes tipos de props que tenemos y como deberíamos probar cada uno.
De forma muy general, las props (properties) son valores que pasamos a nuestros componentes para realizar una tarea específica como modificar el comportamiento o apariencia.
NOTA: Si quieres saber sobre buenas prácticas en los props, tengo un artículo que puede ayudarte: React props: buenas prácticas para código más limpio y escalable
Entre los props más comunes que podemos tener son: props de acción externa, de control interno y de visibilidad.
Veamos de que trata cada uno:
Como sabemos los props permiten modificar el comportamiento de un componente. Este comportamiento, puede incluir lógica de negocio como ocultar o mostrar un campo. Tal vez esto a simple vista no parezca critico, pero puede ocasionar la perdida de un posible cliente.
Alguno de los puntos a considerar de porqué deberíamos probar nuestros props son:
El objetivo de probar este prop, es validar que se ha invocado con los parámetros correctos.
Veamos el siguiente ejemplo:
1: import React, { useState } from "react"; 2: 3: const PeliculasForm = ({ crearPeliculaApi }) => { 4: const [values, setValues] = useState({ 5: nombre: "", 6: }); 7: 8: function handleChange({ target }) { 9: setValues({ ...values, [target.name]: target.value }); 10: } 11: 12: async function onSubmit(e) { 13: e.preventDefault(); 14: await crearPeliculaApi(values); 15: } 16: 17: return ( 18: <form onSubmit={onSubmit}> 19: <input 20: type="text" 21: name="nombre" 22: placeholder="Nombre de la película" 23: data-testid="nombre" 24: value={values.nombre} 25: onChange={handleChange} 26: /> 27: 28: <button type="submit"> 29: Guardar 30: </button> 31: </form> 32: ); 33: }; 34: 35: export default PeliculasForm;
El componente es bastante sencillo, recibe un prop “crearPeliculaApi” que a la hora de invocarse le enviamos la data de la película.
Nuestra prueba, debe validar que esa data se esta pasando a la hora de invocar al prop.
Veamos como luce la prueba:
1: import {fireEvent, render, screen, waitFor} from "@testing-library/react"; 2: import PeliculasForm from "./PeliculasForm"; 3: 4: describe('PeliculasForm', () => { 5: it('crear una pelicula correctamente', async () => { 6: const props = { crearPeliculaApi: jest.fn() } 7: render(<PeliculasForm { ...props } />); 8: 9: fireEvent.change(screen.getByTestId("nombre"), { target: { value: "El silencio de los inocentes" } }); 10: fireEvent.submit(screen.getByText("Guardar")) 11: 12: await waitFor(() => { 13: expect(props.crearPeliculaApi).toHaveBeenCalledWith({ nombre: "El silencio de los inocentes"}); 14: }) 15: }); 16: });
Analicemos
Línea 4
Agrupamos las pruebas para el componente “PeliculasForm”, utilizando la palabra reserva “describe”.
Líne 5
Creamos la prueba “crear un película correctamente”, utilizando la palabra reserva “it”.
Línea 6
Creamos el prop “crearPeliculaApi” y le decimos que se va a comportar como un tipo de doble Spy.
Línea 7
Renderizamos el componente usando la función “render” de React Testing Library.
Línea 9
Escribimos el nombre de la película, usando las funciones screen y fireEvent.
Línea 10
Hacemos clic en el botón Guardar.
Línea 12-14
Valimos que se ha llamado invocado el prop “crearPeliculaApi” con el objeto correctamente.
Para este prop, el objetivo es comprobar que el prop que se asigna a un estado interno cambie el comportamiento del componente.
Analicemos el siguiente código:
1: import React, { useState } from "react"; 2: 3: const PeliculasForm = ({ crearPeliculaApi, esPeliculaParaAdultos }) => { 4: const [values, setValues] = useState({ 5: nombre: "", 6: tipo: esPeliculaParaAdultos ? "adultos" : "toda la familia", 7: }); 8: 9: function handleChange({ target }) { 10: setValues({ ...values, [target.name]: target.value }); 11: } 12: 13: async function onSubmit(e) { 14: e.preventDefault(); 15: await crearPeliculaApi(values); 16: } 17: 18: return ( 19: <form onSubmit={onSubmit}> 20: <input 21: type="text" 22: name="nombre" 23: placeholder="Nombre de la película" 24: data-testid="nombre" 25: value={values.nombre} 26: onChange={handleChange} 27: /> 28: 29: <button type="submit"> 30: Guardar 31: </button> 32: </form> 33: ); 34: }; 35: 36: export default PeliculasForm;
Recibimos el prop “esPeliculaParaAdultos”, que se usa para definir si una película debe ser marcada como apta para toda la familia.
La prueba debe validar que al enviar el valor “true” en el prop “esPeliculaParaAdultos”, el estado “tipo” se le asigne el valor “adultos”.
Veamos la prueba:
. . . 4: describe('PeliculasForm', () => { . . . 20: it('crear una pelicula para adultos correctamente', async () => { 21: const props = { 22: crearPeliculaApi: jest.fn(), 23: esPeliculaParaAdultos: true 24: } 25: render(<PeliculasForm { ...props } />); 26: 27: fireEvent.change(screen.getByTestId("nombre"), { target: { value: "El silencio de los inocentes" } }); 28: fireEvent.submit(screen.getByText("Guardar")) 29: 30: await waitFor(() => { 31: expect(props.crearPeliculaApi).toHaveBeenCalledWith({ 32: nombre: "El silencio de los inocentes", 33: tipo: "adultos", 34: }); 35: }) 36: }); . . .
Analicemos
Línea 23
Definimos el prop “esPeliculaParaAdultos” como verdadero.
Línea 33
Comprobamos que al invocar la función “crearPeliculaApi” se envíe el campo “tipo” con el valor “adultos”.
Prop de visibilidad
Su objetivo es validar si uno o varios elementos se ocultan o se muestran.
Veamos el siguiente código:
1: import React, { useState } from "react"; 2: 3: const PeliculasForm = ({ crearPeliculaApi, esPeliculaParaAdultos, esUsuarioAdmin }) => { 4: const [values, setValues] = useState({ 5: nombre: "", 6: precio: "", 7: tipo: esPeliculaParaAdultos ? "adultos" : "toda la familia", 8: }); 9: 10: function handleChange({ target }) { 11: setValues({ ...values, [target.name]: target.value }); 12: } 13: 14: async function onSubmit(e) { 15: e.preventDefault(); 16: await crearPeliculaApi(values); 17: } 18: 19: return ( 20: <form onSubmit={onSubmit}> 21: <input 22: type="text" 23: name="nombre" 24: placeholder="Nombre de la película" 25: data-testid="nombre" 26: value={values.nombre} 27: onChange={handleChange} 28: /> 29: 30: { 31: esUsuarioAdmin && 32: <input 33: type="text" 34: name="precio" 35: placeholder="Precio" 36: data-testid="precio" 37: value={values.precio} 38: onChange={handleChange} 39: /> 40: } 41: 42: <button type="submit"> 43: Guardar 44: </button> 45: </form> 46: ); 47: }; 48: 49: export default PeliculasForm;
Recibimos el prop “esUsuarioAdmin” para mostrar el campo precio, en caso contrario lo ocultamos.
La prueba es la siguiente:
. . . 38: it('crear una pelicula para usuario admin correctamente', async () => { 39: const props = { 40: crearPeliculaApi: jest.fn(), 41: esPeliculaParaAdultos: true, 42: esUsuarioAdmin: true, 43: } 44: render(<PeliculasForm { ...props } />); 45: 46: fireEvent.change(screen.getByTestId("nombre"), { target: { value: "El silencio de los inocentes" } }); 47: fireEvent.change(screen.getByTestId("precio"), { target: { value: "1000" } }); 48: fireEvent.submit(screen.getByText("Guardar")) 49: 50: await waitFor(() => { 51: expect(props.crearPeliculaApi).toHaveBeenCalledWith({ 52: nombre: "El silencio de los inocentes", 53: tipo: "adultos", 54: precio: "1000", 55: }); 56: }) 57: }); . . .
Analicemos
Línea 42
Definimos que el usuario es un admin para poder asignar un precio a la pelicula, para hacerlo asignamos true al prop “esUsuarioAdmin”.
Línea 47
Asignamos un precio a la película.
Línea 54
Al invocar el prop “crearPeliculaApi”, comprobamos que el campo “precio” venga con el valor asignado en la línea 47.
Estos son algunos de los errores más comunes que podemos encontrarnos:
Para evitarlos, debemos enfocarnos en la lógica de negocio, como lo hicimos en nuestro caso práctico.
Los props son una parte muy importante en nuestros componentes, por lo que probar cada comportamiento que nuestros props generan es vital para tener componentes fáciles de mantener. Sin embargo, evitemos probar comportamientos que no sean tan críticos para el negocio.
Si quieres aprender más sobre pruebas Testing en React con Jest y Testing Library, te dejo estos recursos:
Programa mentalidad de pruebas
Libro: Testing en React: Guía práctica con Jest y React Testing Library