Por qué usar Testing Library en lugar de Enzyme

lucasbernalte

Lucas Bernalte

Posted on February 16, 2021

Por qué usar Testing Library en lugar de Enzyme

Original: https://lucasbernalte.com/blog/por-que-usar-testing-library-en-lugar-de-enzyme

La librería de testing creada por Kent C. Dodds y mantenida por su comunidad, Testing Library (no podían haber elegido un mejor nombre) ha tenido un tremendo éxito en la comunidad de desarrollo y aun empezando solo con React, ya existen Testing Libraries para Svelte, Vue, Angular, Cypress, etc. Antes de que existiera, los desarrolladores de React utilizaban Enzyme, creada por AirBnB, como su librería de testing.

En este artículo voy a explicarte qué hacen ambas librerías y por qué elegir Testing Library sobre Enzyme como tu librería de testing en tu stack de desarrollo.

Enzyme

Enzyme es una librería de utilidades de Testing para React, que hace fácil la lectura del output de nuestros componentes, teniendo una API intuitiva y flexible.

¿Cómo renderiza Enzyme un componente?

  • Shallow rendering: para limitar el test de un componente a su propia unidad, es decir, un nivel de profundidad, sin incluir nada de lo que está por debajo.
const MyComponent = () => {
    return (
        <div>
            <h1>Title</h1>
            <Subcomponent type="info" />
        </div>
    )
};

// output de shallow(<MyComponent />) no renderiza Subcomponent
/*
<div>
    <h1>Title</h1>
    <Subcomponent type="info" />
</div>
*/
Enter fullscreen mode Exit fullscreen mode
  • Full DOM Rendering: Es la única opción de Enzyme que monta el componente, así que tiene que hacerlo en al menos, algo parecido a un browser, como el JSDOM.
  • Static Rendering API: Genera HTML a partir del árbol de React de tu componente, para poder analizar la estructura HTML resultante.

¿Qué podemos comprobar y testear con Enzyme?

  • Podemos interactuar con los elementos de dentro de nuestro componente como sus props y métodos, incluso hacer que se actualice, de forma imperativa:
const wrapper = mount(<SomeComponent />);
act(() => wrapper.prop('handler')());
wrapper.update();
Enter fullscreen mode Exit fullscreen mode
  • Podemos también setear sus props con setProps o sus estado, con setState.
const wrapper = mount(<Foo name="foo" />);
expect(wrapper.find('.foo')).to.have.lengthOf(1);
expect(wrapper.find('.bar')).to.have.lengthOf(0);
wrapper.setProps({ name: 'bar' });
expect(wrapper.find('.foo')).to.have.lengthOf(0);
expect(wrapper.find('.bar')).to.have.lengthOf(1);
Enter fullscreen mode Exit fullscreen mode
  • Simular algunos eventos.
wrapper.find('a').simulate('click');
Enter fullscreen mode Exit fullscreen mode
  • Desmontar el componente.
wrapper.unmount();
Enter fullscreen mode Exit fullscreen mode

Testing Library

Testing Library es un set de utilidades de testing simple y completo que promueve las buenas prácticas del testing. Es una ayuda para testear User Interfaces de forma centrada en el usuario.

¿Cómo renderiza React Testing Library un componente?

Hablamos concretamente de React Testing Library, y no de Testing Library a secas, para hacer la comparación con Enzyme, ya que en otro framework, el renderizado sería diferente. React Testing Library usa el mismo método que usamos para renderizar un componente en nuestra aplicación, ReactDOM.render.

Esto hace que cuando se renderiza un componente en el test, pasa por los mismos ciclos de vida que pasa cuando se renderiza "en la vida real" ya que se hacen de la misma forma.

React Testing Library te provee de una función render que hará este renderizado por ti y te devolvera un tipo de dato que contiene queries para hacerle a este componente.

Aquí puedes ver dónde se realiza el render dentro del código de RTL

¿Qué podemos comprobar y testear con Testing Library?

Testing Library tiene como premisa evitar testear implementaciones, así que lo que podemos hacer es interactuar con el DOM o en React, con el JSDOM. Esto implica que podemos:

  • Comprobar que existe un elemento con queries directamente
document.querySelector('.component-class');
Enter fullscreen mode Exit fullscreen mode
  • Lanzar eventos, al igual que se lanzan eventos desde el DOM cuando el usuario interactúa con el componente.
fireEvent.click(screen.getByRole('button'))
Enter fullscreen mode Exit fullscreen mode
  • Comprobar que existe un elemento con las queries recomendadas, para seguir las buenas prácticas, y que las encontramos como respuesta del método render o dentro de un objeto screen, que contiene todo lo que renderizamos en el test. Esto podemos hacerlo de forma síncrona, o asíncrona. El método asíncrono, en realidad internamente, lo intentaría ejecutar varias veces, teniendo un timeout configurable a nivel global o por ejecución.
render(<MyComponent />);
screen.getByText('title'); // síncrono. Lanza error si no existe.
screen.queryByText('title'); // síncrono. Devuelve null si no existe.
await screen.findByText('title'); // asíncrono.
Enter fullscreen mode Exit fullscreen mode
  • Comprobar que un elemento ha dejado de existir en el DOM:
await waitForElementToBeRemoved(() => screen.queryByText('the mummy'))
Enter fullscreen mode Exit fullscreen mode

Para saber qué query utilizar en cada momento, su documentación nos dice cuáles son las más recomendadas: https://testing-library.com/docs/queries/about#priority

Si necesitamos debuggear un test porque no sabemos qué se está renderizando y tenemos un árbol demasiado grande, no podremos ver bien el resultado en la terminal y se acabará cortando el árbol. Para ello, han desarrollado una herramienta visual, Testing Playground donde podemos visualizar nuestro componente (aunque no tenga los estilos demasiado bien) y ver el árbol completo. Para ello, dentro de nuestro test ponemos:

screen.logTestingPlaygroundURL();
Enter fullscreen mode Exit fullscreen mode

y ya nos saldrá por consola un link que nos llevará a dicha herramienta con nuestro HTML.

Por qué la comunidad está migrando a Testing Library

Si comparamos la opción de Testing Library con el "mount" de Enzyme, serían las más similares. ¿Por qué entonces la comunidad está eligiendo una frente a la otra?

Pues bien, antes de contestar a esta pregunta, es necesario hacerse otra pregunta.

¿Cuántos usuarios tiene nuestro componente?

En la mayoría de los casos, un componente tendrá dos usuarios:

  • Por un lado, el usuario final, que ve nuestra aplicación e interactúa con él.
  • El desarrollador que mantiene el código y puede modificarlo, o reutilizarlo en otro sitio.

Cuando agregamos opciones como shallow rendering, o mocks para testear nuestro componente de forma aislada, estamos creando un tercer usuario de nuestro componente: el test. Esto implica que este test necesitará mantenimiento, estará de alguna medida enlazado con la implementación, y cuando ésta cambie, tendremos que cambiar nuestro test. Nuestro código se vuelve mucho más mantenible cuando sólo tenemos dos usuarios.

Testing Library directamente no da una opción shallow precisamente porque su premisa es hacer tests de una forma "Behavior Driven". Esta forma de testear, aunque parece ser "opinionated", es en realidad la forma más efectiva de testear User Interfaces, evitando testear implementaciones.

"We try to only expose methods and utilities that encourage you to write tests that closely resemble how your React components are used."

Tanto es así, que pasó a ser la primera librería de testing recomendada en la documentación oficial de React.

AirBnB, el propio creador de Enzyme, vio el potencial y comenzó su adopción, manteniendo de momento ambas librerías (tenían más de 17000 tests en Enzyme, imposible migrarlo en un commit...). Y en marzo de 2020 AirBnB anunció que iban a hacer el transfer del ownership de la librería, aunque pensaban seguir contribuyendo.

Algunas de los repositorios Open Source más grandes en Github como Storybook o Gatsby ya usan React Testing Library, y algunas como Material UI están desde abril en proceso de migración.

React va a día de hoy por la v17, y Enzyme, al basarse en la propia implementación de React para poder renderizar, necesita de un "adapter" para funcionar, y todavía no han sacado ninguno oficial para la v17, teniendo que instalarse un adapter no-oficial para que pueda funcionar con la última versión.

Mi recomendación

Cada librería tiene su contexto, y hay que entender el contexto y la premisa con la que fueron creadas. La adopción de testear comportamientos por parte de la comunidad es muy grande y es la forma de aportar más valor con tus tests. Yo recomiendo elegir Testing Library para evitar caer en la tentación y testear cosas que no deberías, como props que se pasan de un sitio a otro (estarías testeando la propia implementación de React). Además proporciona una API muy buena e intuitiva para comprobar nuestra UI incluso de forma asíncrona, y la comunidad está haciendo un gran trabajo, extrayendo el core de la librería y sacando a utilidades toda su API para que pueda ser utilizada con cualquier framework. Y también están desarrollando user-event una librería que simula los eventos reales que realizaría un usuario como "click", "doble-click", "escribir", etc.


Si te ha gustado este artículo, puedes ayudarme dándole visibilidad. Sígueme en Twitter para estar al tanto del contenido que voy creando.

🙋‍♂️ Happy Testing!

💖 💪 🙅 🚩
lucasbernalte
Lucas Bernalte

Posted on February 16, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related