Probando componentes en reaccionar usando broma y enzima

Esta es la segunda parte de la serie sobre componentes de prueba en React. Si tiene experiencia previa con Jest, puede saltar y usar el código de GitHub como punto de partida.. 

En el artículo anterior, cubrimos los principios básicos y las ideas detrás del desarrollo basado en pruebas. También configuramos el entorno y las herramientas necesarias para ejecutar pruebas en React. El conjunto de herramientas incluía Jest, ReactTestUtils, Enzyme y react-test-renderer. 

Luego escribimos un par de pruebas para una aplicación de demostración utilizando ReactTestUtils y descubrimos sus defectos en comparación con una biblioteca más robusta como Enzyme..

En esta publicación, obtendremos una comprensión más profunda de los componentes de prueba en React al escribir pruebas más prácticas y realistas. Puedes dirigirte a GitHub y clonar mi repo antes de comenzar.

Comenzando con la API de enzimas

Enzyme.js es una biblioteca de código abierto mantenida por Airbnb, y es un gran recurso para los desarrolladores de React. Utiliza la API ReactTestUtils debajo, pero a diferencia de ReactTestUtils, Enzyme ofrece una API de alto nivel y una sintaxis fácil de entender. Instala Enzyme si aún no lo has hecho.

La API de enzimas exporta tres tipos de opciones de representación:

  1. representación superficial
  2. representación completa de DOM
  3. representación estática

Representación superficial Se utiliza para hacer un componente particular de forma aislada. Los componentes secundarios no se procesarán y, por lo tanto, no podrá hacer valer su comportamiento. Si vas a centrarte en las pruebas unitarias, te encantará esto. Puedes hacer un componente como este:

importar superficial desde 'enzima'; importar ProductHeader desde './ProductHeader'; // Más ejemplo concreto más abajo. componente const = poco profundo (); 

Representación completa de DOM genera un DOM virtual del componente con la ayuda de una biblioteca llamada jsdom. Puede aprovechar esta característica reemplazando el superficial() método con montar() en el ejemplo anterior. El beneficio obvio es que también puede renderizar los componentes secundarios. Si desea probar el comportamiento de un componente con sus hijos, debe utilizar este. 

Representación estática Se utiliza para hacer reaccionar componentes a HTML estático. Se implementa utilizando una biblioteca llamada Cheerio, y puedes leer más sobre esto en los documentos.. 

Revisando nuestras pruebas anteriores

Aquí están las pruebas que escribimos en el último tutorial:

src / components / __ tests __ / ProductHeader.test.js

importar ReactTestUtils desde 'react-dom / test-utils'; // ES6 describe ('ProductHeader Component', () => it ('tiene una etiqueta h2', () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithTag (componente, 'h2'); ); it ('tiene una clase de título', () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithClass (componente, 'título'); ))

La primera prueba comprueba si el ProducerHeader componente tiene un

etiqueta, y la segunda encuentra si tiene una clase CSS nombrada título. El código es difícil de leer y entender.. 

Aquí están las pruebas reescritas usando enzima.

src / components / __ tests __ / ProductHeader.test.js

importar superficial desde 'enzima' describe ('ProductHeader Component', () => it ('tiene una etiqueta h2', () => const component = shallow (); var node = component.find ('h2'); expect (node.length) .toEqual (1); ); it ('tiene una clase de título', () => const component = shallow (); var node = component.find ('h2'); expect (node.hasClass ('title')). toBeTruthy (); ))

Primero, creé un DOM de poca profundidad  componente usando superficial() y lo almacenamos en una variable. Entonces, utilicé el .encontrar() Método para encontrar un nodo con la etiqueta 'h2'. Pregunta al DOM para ver si hay una coincidencia. Como solo hay una instancia del nodo, podemos asumir con seguridad que node.length será igual a 1.

La segunda prueba es muy similar a la primera. los hasClass ('título') método devuelve si el nodo actual tiene una nombre de la clase prop con valor 'título'. Podemos verificar la veracidad utilizando toBeTruthy ().  

Ejecutar las pruebas utilizando prueba de hilo, y ambas pruebas deben pasar. 

¡Bien hecho! Ahora es el momento de refactorizar el código. Esto es importante desde la perspectiva de un probador porque las pruebas legibles son más fáciles de mantener. En las pruebas anteriores, las dos primeras líneas son idénticas para ambas pruebas. Puedes refactorizarlos usando un antes de cada () función. Como su nombre lo indica, el antes de cada La función se llama una vez antes de que se ejecute cada especificación en un bloque de descripción. 

Puedes pasar una función de flecha a antes de cada () Me gusta esto.

src / components / __ tests __ / ProductHeader.test.js

importar superficial desde 'enzima' describe ('ProductHeader Component', () => let component, node; // Jest antes de Cada () antes de Cada ((() => componente = superficial))) beforeEach ((() => node = component.find ('h2'))) it ('tiene una etiqueta h2', () => expect (node) .toBeTruthy ()); it ('tiene una clase de título', () => expect (node.hasClass ('title')). toBeTruthy ()))

Pruebas unitarias de escritura con broma y enzima

Vamos a escribir algunas pruebas unitarias para el Detalles del producto componente. Es un componente de presentación que muestra los detalles de cada producto individual.. 

Vamos a probar la sección que está resaltada.

La prueba de unidad intentará hacer valer las siguientes suposiciones:

  • El componente existe y los accesorios se pasan..
  • Se muestran los accesorios como el nombre del producto, la descripción y la disponibilidad..
  • Aparece un mensaje de error cuando los accesorios están vacíos..

Aquí está la estructura básica de la prueba. El primero antes de cada () almacena los datos del producto en una variable, y el segundo monta el componente.

src / components / __ tests __ / ProductDetails.test.js

describe ("componente ProductDetails", () => var component, producto; beforeEach (() => producto = id: 1, nombre: 'NIKE Liteforce Blue Sneakers', descripción: 'Lorem ipsum.', estado: 'Disponible';) antes de Cada (() => component = mount (); ) ('prueba # 1', () => ))

La primera prueba es fácil:

it ('debería existir', () => expect (component) .toBeTruthy (); expect (component.props (). product) .toEqual (product);)

Aquí usamos el accesorios() Método que es útil para obtener los accesorios de un componente..

Para la segunda prueba, puede consultar los elementos por sus nombres de clase y luego verificar si el nombre, la descripción, etc. del producto son parte de los elementos. texto interior

 it ('debería mostrar datos del producto cuando se pasan los props', () => let title = component.find ('. product-title'); expect (title.text ()). toEqual (product.name); let description = component.find ('. product-description'); expect (description.text ()). toEqual (product.description);) 

los texto() El método es particularmente útil en este caso para recuperar el texto interno de un elemento. Intenta escribir una expectativa para el Estado del producto() y ver si todas las pruebas están pasando.

Para la prueba final, vamos a montar el Detalles del producto Componente sin puntales. Luego buscaremos una clase llamada '.product-error' y verificaremos si contiene el texto "Disculpe, el producto no existe"..

 it ('debería mostrar un error cuando no se pasan los props', () => / * componente sin props * / component = mount (); deje node = component.find ('. product-error'); expect (node.text ()). toEqual ('Sorry. El producto no existe'); ) 

Eso es. Hemos probado con éxito el Componente en aislamiento. Las pruebas de este tipo se conocen como pruebas unitarias..

Prueba de devoluciones de llamada utilizando apéndices y espías

Acabamos de aprender a probar accesorios. Pero para probar verdaderamente un componente aislado, también necesita probar las funciones de devolución de llamada. En esta sección, escribiremos pruebas para el Lista de productos componente y crear apéndices para funciones de devolución de llamada en el camino. Aquí están los supuestos que tenemos que afirmar.

  1. La cantidad de productos enumerados debe ser equivalente a la cantidad de objetos que el componente recibe como accesorios.
  2. Haciendo clic en debe invocar la función de devolución de llamada.

Vamos a crear un antes de cada () Función que completa datos de productos simulados para nuestras pruebas..

src / components / __ tests __ / ProductList.test.js

 beforeEach (() => productData = [id: 1, nombre: 'NIKE Liteforce Blue Sneakers', descripción: 'Lorem ipsu.', estado: 'Disponible', // Omitido por brevedad])

Ahora, vamos a montar nuestro componente en otro antes de cada () bloquear.

beforeEach (() => handleProductClick = jest.fn (); component = mount (  ); )

los Lista de productos Recibe los datos del producto a través de accesorios. Además de eso, recibe una devolución de llamada del padre. Aunque podría escribir pruebas para la función de devolución de llamada de los padres, no es una buena idea si su objetivo es atenerse a las pruebas unitarias. Como la función de devolución de llamada pertenece al componente principal, la incorporación de la lógica de los padres complicará las pruebas. En su lugar, vamos a crear una función de código auxiliar..

Que es un trozo? 

Un talón es una función ficticia que pretende ser otra función. Esto le permite probar de forma independiente un componente sin importar componentes principales o secundarios. En el ejemplo anterior, creamos una función de código auxiliar llamada manejarProductClick invocando jest.fn ()

Ahora solo tenemos que encontrar todos los elementos en el DOM y simular un clic en la primera nodo. Después de hacer clic, comprobaremos si manejarProductClick () fue invocado Si es así, es justo decir que nuestra lógica funciona como se espera.

it ('debería llamar a selectProduct cuando se haga clic en', () => const firstLink = component.find ('a'). first (); firstLink.simulate ('click'); expect (handleProductClick.mock.calls.length) .toEqual (1);))

Enzyme te permite simular fácilmente acciones de usuarios como clics usando simular() método. handlerProductClick.mock.calls.length devuelve el número de veces que se llamó a la función simulada. Esperamos que sea igual a 1.

La otra prueba es relativamente fácil. Puedes usar el encontrar() método para recuperar todo Nodos en el DOM. El número de Los nodos deben ser iguales a la longitud de la matriz productData que creamos anteriormente. 

 it ('debería mostrar todos los elementos del producto', () => let links = component.find ('a'); expect (links.length) .toEqual (productData.length);) 

Prueba del estado del componente, LifeCycleHook y el método

A continuación, vamos a probar el ProductContainer componente. Tiene un estado, un gancho de ciclo de vida y un método de clase. Aquí están las afirmaciones que deben ser verificadas:

  1. componentDidMount se llama exactamente una vez.
  2. El estado del componente se rellena después de que el componente se monta.
  3. los manejarProductClick () El método debe actualizar el estado cuando se pasa un ID de producto como argumento.

Para comprobar si componentDidMount Fue llamado, vamos a espiarlo. A diferencia de un código auxiliar, se usa un espía cuando necesita probar una función existente. Una vez que se configura el espía, puede escribir aserciones para confirmar si la función fue llamada.

Puedes espiar una función de la siguiente manera:

src / components / __ tests __ / ProductContainer.test.js

 it ('debería llamar a componentDidMount once', () => componentDidMountSpy = spyOn (ProductContainer.prototype, 'componentDidMount'); // Para finalizar);

El primer parámetro para jest.spyOn es un objeto que define el prototipo de la clase que estamos espiando. El segundo es el nombre del método que queremos espiar.. 

Ahora renderice el componente y cree una aserción para verificar si se llamó al espía.

 componente = superficial (); expect (componentDidMountSpy) .toHaveBeenCalledTimes (1);

Para verificar que el estado del componente se complete después de que el componente se monte, podemos usar Enzyme's estado() Método para recuperar todo en el estado.. 

it ('debería llenar el estado', () => component = shallow (); expect (component.state (). productList.length) .toEqual (4))

El tercero es un poco complicado. Necesitamos verificar que manejarProductClick está trabajando como se esperaba. Si te diriges al código, verás que el manejarProductClick () método toma una identificación del producto como entrada, y luego actualiza this.state.selectedProduct con los detalles de ese producto. 

Para probar esto, necesitamos invocar el método del componente, y usted puede hacerlo llamando al component.instance (). handleProductClick (). Pasaremos en una identificación de producto de muestra. En el siguiente ejemplo, utilizamos el id del primer producto. Luego, podemos probar si el estado se actualizó para confirmar que la afirmación es verdadera. Aquí está el código completo:

 it ('debería tener un método de trabajo llamado handleProductClick', () => let firstProduct = productData [0] .id; component = shallow (); component.instance (). handleProductClick (firstProduct); expect (component.state (). selectedProduct) .toEqual (productData [0]); ) 

Hemos escrito 10 pruebas, y si todo va bien, esto es lo que deberías ver:

Resumen

¡Uf! Hemos cubierto casi todo lo que necesita saber para comenzar con las pruebas de escritura en React utilizando Jest y Enzyme. Ahora puede ser un buen momento para dirigirse al sitio web de Enzyme para tener una visión más profunda de su API.

¿Qué piensas sobre las pruebas de escritura en React? Me encantaría escucharlos en los comentarios..