
Post original:https://alextomas.com/blog/primeros-pasos-test-enzyme
Algo que siempre hemos dejado para última hora, o bien dejamos de hacerlo:los tests. Empezaré diciendo que yo también soy nuevo en los tests y que esto será un resumen de lo que voy aprendiendo en el tema testing 😜
Hace unos días entré en un nuevo proyecto y se quiere dejar testeado muchas cosas, con cierto criterio, así que me he puesto a investigar la libreríaEnzyme con el objetivo de testear algunos componentes de React. La curva de aprendizaje es relativamente sencilla, así que voy a intentar explicar algunos conceptos básico para ir quitándonos el miedo a los tests.
Índice de contenidos:
- Instalación de Enzyme en React
- React 16
- React 17
- Snapshots
- Configuración de Enzyme en React
- Configuración de Enzyme para React 16
- Configuración de Enzyme para React 17
- Componente de ejemplo para los tests:
- Tests que vamos a realizar sobre el componente
- Testing
- Importamos las dependencias
- ¿Qué es describe?
- Los tests, ¡por fin!
- Test 1: debería mostrar correctamente
- Test 2: debe de mostrar el valor por defecto de 100
- Test 3: debe incrementar con el botón +1
- Test 4: debe decrementar con el botón -1
- Test 5: debe de colocar el valor por defecto con el botón reset
- Resultado final de los tests
- beforeEach
- Conclusiones
Instalación de Enzyme en React
Lo primero que debemos de mirar es ladocumentación de Enzyme para la instalación, y aquí haremos una matización.
React 16
Si tienes la versión 16 de React, te servirá la documentación actual (este documento lo estoy escribiendo el 8 de febrero del 2021). Si no sabes que versión de React estás usando, ve alpackage.json
y verás en las dependencias algo como:
Si este es tu caso la instalación sería de la siguiente forma:
npm i--save-dev enzyme enzyme-adapter-react-16
React 17
Si tienes la versión 17 de React, tendraás que hacer un pequeño cambio, ya que oficialmente Enzyme no da soporte a la versión 17 (este documento lo estoy escribiendo el 8 de febrero del 2021).
Nota: Si cuando leas este artículo React ya da soporte para la versión 17 no es necesario que hagas la configuración de este modo
Si este es tu caso la instalación sería de la siguiente forma:
npm i--save-dev enzyme
Y luego necesitaremos el adaptador para la versión 17. No es un adaptador oficial, peroWojciech Maj nos ha dejado uno, de momento, no oficial.
npminstall--save-dev @wojtekmaj/enzyme-adapter-react-17
Snapshots
Sólo queda una cosa más. Para poder hacer "capturas" de nuestros componentes y guardarlos en snapshots para hacer ciertas pruebas, vamos a necesitar un paquete que se llamaenzyme-to-json
y lo puedes instalar de la siguiente forma:
npminstall--save-dev enzyme-to-json
Configuración de Enzyme en React
Esto es una de las mejores cosas, lo fácil que es configurar Enzyme en React. Simplemente abre el archivosrc/setupTests.js
y lo dejaremos de la siguiente manera (si no tienes este archivo, créalo):
Configuración de Enzyme para React 16
importEnzymefrom'enzyme';importAdapterfrom'enzyme-adapter-react-16';import{createSerializer}from'enzyme-to-json';Enzyme.configure({adapter:newAdapter()});expect.addSnapshotSerializer(createSerializer({mode:'deep'}));
Configuración de Enzyme para React 17
Nota: Si cuando leas este artículo React ya da soporte para la versión 17 no es necesario que hagas la configuración de este modo
importEnzymefrom'enzyme';importAdapterfrom'@wojtekmaj/enzyme-adapter-react-17';import{createSerializer}from'enzyme-to-json';Enzyme.configure({adapter:newAdapter()});expect.addSnapshotSerializer(createSerializer({mode:'deep'}));
¡Perfecto! 🎉 🚀 Ahora ya lo tenemos todo listo para empezar con nuestros tests.
Componente de ejemplo para los tests:
Bien, para nuestro ejemplo vamos a utilizar el clásico ejemplo de un contador. Básicamente tendrá tres acciones:
- Botón para aumentar +1 el contador
- Botón para resetear el contador
- Botón para restar -1 el contador
Quedando así:
importReact,{useState}from'react';exportconstCounterApp=({value=10})=>{const[counter,setCounter]=useState(value);consthandleUp=()=>setCounter((counterPref)=>counterPref+1);consthandleDown=()=>setCounter((counterPref)=>counterPref-1);consthandleReset=()=>setCounter(value);return(<><h1>CounterApp</h1><div><h2>{counter}</h2><div><buttononClick={handleUp}>+1</button><buttononClick={handleReset}>Reset</button><buttononClick={handleDown}>-1</button></div></div></>);};
Y lo usamos de la siguiente manera:
<CounterAppvalue="{100}"/>
Y visualmente quedaría algo como:
Tests que vamos a realizar sobre el componente
Bien, las pruebas que vamos a hacer serán las siguientes:
- Se debería mostrar correctamente.
- Debe de mostrar el valor por defecto de 100
- Debe incrementar con el botón +1
- Debe decrementar con el botón -1
- Debe de colocar el valor por defecto con el botón reset
Testing
Primero pongo todo el test como va a quedar y lo iré explicando. Lo primero que debemos de crearnos es una carpeta donde iremos poniendo todos los tests, en mi caso, me he creado una carpetatests
(en plural porque habrá más de uno) y dentro he colocado unCounterApp.test.js
. EsMUY IMPORTANTE poner en el nombre.test
porque sino React no se enterará de que eso es un test como tal. No lo olvides.
import'@testing-library/jest-dom';import{shallow}from'enzyme';import{CounterApp}from'../CounterApp';describe('Probamos el componente <CounterApp />',()=>{letwrapper=shallow(<CounterApp/>);beforeEach(()=>{wrapper=shallow(<CounterApp/>);});test('debería mostrar <CounterApp /> correctamente',()=>{expect(wrapper).toMatchSnapshot();});test('debe de mostrar el valor por defecto de 100',()=>{constwrapper=shallow(<CounterAppvalue={100}/>);constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('100');});test('debe incrementar con el botón +1',()=>{wrapper.find('button').at(0).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('11');});test('debe decrementar con el botón -1',()=>{wrapper.find('button').at(2).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('9');});test('debe de colocar el valor por defecto con el botón reset',()=>{constwrapper=shallow(<CounterAppvalue={105}/>);wrapper.find('button').at(0).simulate('click');wrapper.find('button').at(1).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('105');});});
Bien, vamos a explicar todo un poco.
Importamos las dependencias
import'@testing-library/jest-dom';import{shallow}from'enzyme';import{CounterApp}from'../CounterApp';
Esto no nos va a sorprender, ¿verdad?
@testing-library/jest-dom
dependencia no es obligatoria importarla, pero sirecomendable porque así nos habilitará elIntelliSense para los tests, y esto es algo muy cómodo para no tener que ir recordando todos los nombres de las funciones y demás.enzyme
es la librería con el core de enzyme para los tests. Importación obligatoria.CounterApp
es nuestro componente con su ruta relativa que vamos a testear.
¿Qué es describe?
´describe´ nos permite agrupar uno o varios tests para que de algún modo quede todo más legible y ordenado. En nuestro caso vamos a hacer un grupo (describe) con varias pruebas dentro de el mismo (test)
describe('Probamos el componente <CounterApp />',()=>{// Aquí dentro irán los tests para este grupo});
Los tests, ¡por fin!
letwrapper=shallow(<CounterApp/>);beforeEach(()=>{wrapper=shallow(<CounterApp/>);});
shallow
es una función de Enzyme que se utiliza para probar componentes de forma aislada, ya que no renderiza los subcomponentes. Si deseas renderizar los subcomponentes utilizarender omount.
Así pues, con enwrapper
nos estamos "guardando" el componente para poder usarlo en las siguientes pruebas.
Con elbeforeEach
lo que le decimos que el componente se reinicie al estado inicial cada vez que inicia una nueva prueba (esto lo explicaré luego).
Nota: Observarás que duplico la línea para definir elwrapper
con el shallow (shallow(<CounterApp />)
). No es lo más bonito la verdad, pero es la forma para mantener elIntelliSense a lo largo del archivo de tests. Si conoces una forma más limpia estoy abierto a todos los comentarios 🙃
Test 1: debería mostrar correctamente
Ahora entramos en materia buena. Lo primero que te recomiendo es pegarle un vistazo a la documentación sobreexpect para que veas todas las cosas que podemos hacer con él.
test('debería mostrar <CounterApp /> correctamente',()=>{expect(wrapper).toMatchSnapshot();});
Con esto le decimos al test que esperamos que el componente se renderice correctamente y nos cree nuestro snapshot. ¿Qué es un snapshot? Pues básicamente una copia del html resultante que genera el componente. Verás que ahora tienes una nueva carpeta ensrc/tests/__snapshots__
con un archivosrc/tests/__snapshots__/CounterApp.test.js.snap
que se verá así:
// Jest Snapshot v1, https://goo.gl/fbAQLPexports[`Probamos el componente <CounterApp /> debería mostrar <CounterApp /> correctamente 1`]=`<Fragment> <h1> Counter App </h1> <div> <h2> 10 </h2> <div> <button onClick={[Function]} > +1 </button> <button onClick={[Function]} > Reset </button> <button onClick={[Function]} > -1 </button> </div> </div></Fragment>`;
Si todo esto es correcto, perfecto, vamos bien 😌
Test 2: debe de mostrar el valor por defecto de 100
test('debe de mostrar el valor por defecto de 100',()=>{constwrapper=shallow(<CounterAppvalue={100}/>);constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('100');});
Aquí instanciamos de nuevo el componente y se lo asignamos a una variablewrapper
, pero.... ¿porqué no usamos el wrapper que definimos al principio? Mira las diferencias:
constwrapper=shallow(<CounterAppvalue={100}/>);
letwrapper=shallow(<CounterApp/>);
En este nuevo caso necesitamos pasarle porprops
el valos concreto que queremos, en este caso el 100 (por defecto el componente coge el valor 10, recuerda la definición del componente que eraexport const CounterApp = ({ value = 10 })
)
Lo siguiente, elcounterText
es una variable en la que queremos guardar el texto que contiene la etiqueta h2 de nuestro componente. Si recordamos nuestro componente, tenemos:
<h2>{counter}</h2>
Entonces, conwrapper.find('h2').text().trim()
le decimos que busque la etiqueta<h2>
, obtenga su texto contenido y le aplica un trim por si acaso tiene espacios en blanco delante o detrás. Esto, como verás, es muy parecido al jQuery 🤨
Finalmente hacemos la comprobación:expect(counterText).toBe('100')
que básicamente es "preguntarle" a counterText si es === '100'.
Test 3: debe incrementar con el botón +1
test('debe incrementar con el botón +1',()=>{wrapper.find('button').at(0).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('11');});
Lo primero que debemos obtener es el botón +1. Recordemos nuestro componente:
<buttononClick="{handleUp}">+1</button><buttononClick="{handleReset}">Reset</button><buttononClick="{handleDown}">-1</button>
Cuando hacemoswrapper.find('button')
obtenemos todos los botones de nuestro componente y nos los guarda en un array. Así pues, en la posición 0 estará el +1, en la posición 1 estará el reset y en la posición 2 estará el -1. Fácil, ¿no?
Entonces, capturamos el botón +1 y simulamos unclick
de la siguiente forma:wrapper.find('button').at(0).simulate('click')
y buscamos de nuevo el valor que contiene la etiqueta h2 y lo verificamos:expect(counterText).toBe('11')
Test 4: debe decrementar con el botón -1
test('debe decrementar con el botón -1',()=>{wrapper.find('button').at(2).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('9');});
Entonces, capturamos el botón -1 y simulamos unclick
de la siguiente forma:wrapper.find('button').at(2).simulate('click')
y buscamos de nuevo el valor que contiene la etiqueta h2 y lo verificamos:expect(counterText).toBe('9')
. Fácil.
Test 5: debe de colocar el valor por defecto con el botón reset
test('debe de colocar el valor por defecto con el botón reset',()=>{constwrapper=shallow(<CounterAppvalue={105}/>);wrapper.find('button').at(0).simulate('click');wrapper.find('button').at(1).simulate('click');constcounterText=wrapper.find('h2').text().trim();expect(counterText).toBe('105');});
Este test nos servirá para comprobar que el valor vuelve a ser el que le hemos pasado una vez le hemos sumado +1 y le pulsamos el botón reset. Del código de este test ya nos debería de sonar todo:
Primero, definimos un nuevo wrapper porque queremos pasarle un valor por defecto, para nuestro ejemplo será el 105. Luego pulsamos el botón de la posición 0 que es el de sumar +1 (ahora el valor en el componente será de 106).
Luego hacemos otro click, el del botón de la posición 1 que es el deReset para que vuelva al valor pasado por props (105). Y obtenemos el valor de nuevo de la etiqueta h2. ¿Resultado? 105 😉
Resultado final de los tests
Si todo ha ido bien deberías de ver todos los checks verdes.
¡Es hora de celebrarlo! 🎉
beforeEach
Prueba a comentar la línea del beforeEach:
beforeEach(()=>{wrapper=shallow(<CounterApp/>);});
Y vuelve a correr los tests.
¿Que ha pasado? ¡Pero si no he tocado nada del código! La explicación es sencilla, y verás que tiene su lógica.
Todos los tests se ejecutan de forma secuencial. Como verás nos ha fallado el test al comprobar el valor cuando restamos -1. El test esperaba recibir el valor de 9, pero en cambio recibe un valor de 10. ¡¿WTF?! Recuerda, el valor por defecto es de 10, en la pruebadebe incrementar con el botón +1
le hemos sumado +1 (11), y en la siguientedebe decrementar con el botón -1
le hemos restado -1 a ese valor 11 que teníamos de la prueba anterior, por tanto tenemos el 10. De ahí el error.
Entonces, con beforeEach, lo que hacemos es reiniciar el componente en cada test que queremos pasar y así nos aseguramos siempre del estado que queremos tener y esperamos para cada uno de ellos. 👏🏻
Conclusiones
A todos nos gusta picar código desde un primer momento y nos olvidamos de los tests, yo incluido, bien por falta de tiempo del proyecto, o bien por pereza.
Pero para hacer tests no debemos volvernos tampoco locos. Testea con cabeza y no quieras hacer tests de cosas que no te sean necesarias. Verás que la curva de aprendizaje no es alta, y poco a poco le cogerás el punto y ganarás en salud, sobre todo en eso 😂
¡Haz tests! 🙏🏻
Repo:https://github.com/alextomas80/testing-components-enzyme
Y esto es todo. Espero que te pueda servir 😉
Top comments(3)

- Email
- LocationLa Paz, Bolivia
- EducationUniversidad La Salle - Bolivia
- Joined
No me parece correcto escribir un post basado en el curso de React de Fernando Herrara y ni mencionarlo.
For further actions, you may consider blocking this person and/orreporting abuse