Вы находитесь на странице: 1из 7

ReactJS se ha convertido en una de las librerías más populares para crear las interfaces

de usuario de nuestras aplicaciones. Introdujo un nuevo concepto


llamado componentes; y, debido a esto, los mismos creadores de Facebook vieron la
necesidad de encontrar una herramienta para poder hacer tests efectivos de React.

Y así crearon Jest. Se trata de un framework para Unit Testing basado en Jasmine.

- Hace de manera automática mocks de las dependencias CommonJS: puedes declarar


módulos require(“modulo”) en cualquier sección de tus tests.

- Encuentra y ejecuta de manera automática los tests: al ejecutar el


comando Jest, buscará las carpetas tests en tu proyecto y ejecutará todo lo que se
encuentre ahí. Además, puedes configurar el nombre de esa carpeta.

- Ejecuta los tests en una implementación falsa del DOM (usando JsDOM) para poder
correr los tests desde la consola sin necesidad de hacerlo en el navegador.

Instalación

Para poder testear código ES6 debemos de tener instalado Babel global
npm i -g babel

Después, instalamos el cliente de Jest para poder ejecutar los test desde la terminal
npm i -g jest-cli

Luego, instalamos en local las dependencias necesarias para React y Jest


npm i -D babel-jest jest-cli react

Configuración

En el package.json debemos configurar Jest de la siguiente manera:


"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"testFileExtensions": [
"es6",
"js"
],
"moduleFileExtensions": [
"js",
"json",
"es6",
"jsx"
],
"unmockedModulePathPatterns": ["<rootDir>/node_modules/react"]
}

rootDir: es el folder donde jest buscará los tests y los módulos por default. También es
la carpeta que contiene el package.json por defecto.

scriptPreprocessor: definimos que pre-procesador va a ejecutar los tests. En nuestro


caso utilizamos babel-jest para poder interpretar ES6.

testFileExtensions: definimos los tipos de archivos en los que pueden estar escritos los
test.

moduleFileExtensions: definimos los tipos de archivos que los tests pueden


interpretar.Si te interesa conocer más acerca de su configuración, puedes encontrar más
opciones en su API.

Jest en acción

El componente que vamos a testear para el ejemplo será TODO.jsx.


Verificaremos que su funcionamiento sea correcto.
import React from 'react/addons';

const TODO = React.createClass({


propType: {
defaultText: React.PropTypes.string,
},
getDefaultProps() {
return {
defaultText: "task",
};
},
getInitialState() {
return {
items: [],
defaultText: this.props.defaultText,
};
},
onKeyDown(e) {
e.preventDefault();
if (e.keyCode == 13) {
const textItem = this.refs.item.getDOMNode().value.trim();
const isEmpty = (textItem.length === 0);
//this.refs.item.getDOMNode().value = '';
const item = {
text: textItem,
};
const {items} = this.state;
if (!isEmpty) {
items.push(item);
this.setState({items});
}
}
},
onChange(e) {
e.preventDefault();
this.setState({
defaultText: this.refs.item.getDOMNode().value.trim(),
});
},
onDeleteItem(itemText) {
const items = this.state.items.filter((item) => item.text !=
itemText);
this.setState({items});
},
renderList() {
return this.state.items.map((item, index) => {
return (
<li
className="TODO-Item"
key={index}>
<span
className="TODO-ItemDeleteIcon"
onClick={() => {this.onDeleteItem(item.text)}}
>
x
</span>
{item.text}
</li>);
});
},
render() {
const list = this.renderList();
return (
<div className="TODO">
<form className="TODO-Form">
<input
type="text"
onChange={this.onChange}
onKeyDown={this.onKeyDown}
value={this.state.defaultText}
ref="item"
/>
<button type="button" onClick={this.onClick}>
enviar</button>
</form>
<ul className="TODO-List">
{list}
</ul>
</div>
);
},
});
export default TODO;

Los casos a testear serán:

-El componente debe estar definido.

-El input debe existir y estar definido como elemento del DOM.

-Al presionar enter se debería de crear un ítem.


-Al dar clic sobre el ícono de borrado de un ítem, este debería ser eliminado.

-Si presionamos enter para crear un ítem pero el input esta vacío, no debería de crear
elemento alguno.

-La cantidad de ítems debería ser igual que la cantidad de ítems en el state.

Caso 1: El componente debe estar definido

Para poder simular los componentes necesitaremos usar la herramienta TestUtils de


React. La podremos usar al importar react/addons.
import React from 'react/addons';
const {TestUtils} = React.addons;

TestUtils tiene el método renderIntoDocument. Este nos permitirá renderizar


componentes y generar un DOM “falso” con JsDOM; al que le podemos pasar atributos
(props).
const TodoComponent = TestUtils.renderIntoDocument(<TODO
defaultText="new task"/>);

TestUtils cuenta con diferentes métodos para definir el estado de un componente o un


elemento del DOM:

isCompositeComponent: recibe el elemento y retorna true si este es un componente de


React.

isDOMComponent: recibe elementos y retorna true si este pertenece al DOM.El test


para este caso, se debería ver así:

import React from 'react/addons';


const {TestUtils} = React.addons;

jest.dontMock('../TODO.jsx');

describe('TODO', () => {
//definimos el componente a Testear
const TODO = require('../TODO.jsx');
const TodoComponent = TestUtils.renderIntoDocument(<TODO
defaultText="new task"/>);
//con TestUtils renderizamos un componente al cual le podemos
pasar datos
it("El componente debe estar definido", () => {

expect(TestUtils.isCompositeComponent(TodoComponent)).toBeTruthy();
});
});
Usamos jest.dontMock(‘../TODO.jsx’); porque Jest hace mock de cada componente.
En este caso estamos usando el componente real.

Caso 2: El input debe existir y estar definido como elemento del DOM

Para poder encontrar elementos del DOM podemos usar los siguientes métodos
deTestUtils:

findRenderedDOMComponentWithTag: permite encontrar un elemento del DOM


con el tag HTML que se envíe. Por ejemplo, < span > o < h1 >

scryRenderedDOMComponentsWithTag: es igual
que findRenderedDOMComponentsWithTag, pero retorna un array con todos los
elementos con este tag.

findRenderedDOMComponentWithClass: permite encontrar un elemento del DOM


por su clase.

scryRenderedDOMComponentsWithClass: Es igual
que findRenderedDOMComponentsWithClass pero en este caso retorna un array con
todos los elementos con esta clase

.Ahora usaremos el método findRenderedDOMComponentWithTag para encontrar el


input. En este caso, se vería así:

it('El input debe existir y estar definido como elemento del DOM',
() => {
const input =
TestUtils.findRenderedDOMComponentWithTag(TodoComponent, 'input');
expect(TestUtils.isDOMComponent(input)).toBeTruthy();
});

Caso 3: Al presionar enter se debería de crear un ítem

TestUtils tiene un método que nos será muy útil: Simulate.

Este puede emular un evento JavaScript en nuestro componente a testear.

Puede ser click, keyDown, chagne, hover, etc. Simulate recibe como parámetros el
elemento del DOM al que queramos ejercerle la acción y, de manera
opcional, eventData. Este caso se vería así:
it('al enviar el formulario se deberia de crear un item', () => {
//definimos los componentes del DOM
const input =
TestUtils.findRenderedDOMComponentWithTag(TodoComponent, 'input');
//Simulamos cambio en el input
TestUtils.Simulate.change(input);
//Simulamos el keDown de la letra #13 de enter
TestUtils.Simulate.keyDown(input, {key: "Enter", keyCode: 13, which:
13});
//al crear un item, deberia de renderizarse otro elemento de la
lista, por ende,
//aumentar el largo del array de este
const items =
TestUtils.scryRenderedDOMComponentsWithClass(TodoComponent, 'TODO-
Item');
//al crear una tarea, verificamos que el largo sea mayor que cero
expect(items.length > 0).toBeTruthy();
});

Caso 4: Al dar clic sobre el ícono de borrado de un ítem, este debería ser
eliminado

Ahora usaremos un evento click con Simulate. Se debería ver así:

it('Al presionar click sobre el icono de borrado de un item este


deberia ser eliminado', () => {
const Todo = TestUtils.renderIntoDocument(<TODO
defaultText="task"/>);
const input = TestUtils.findRenderedDOMComponentWithTag(Todo,
'input');
//Simulamos la creación de un item.
TestUtils.Simulate.change(input);
TestUtils.Simulate.keyDown(input, {key: "Enter", keyCode: 13, which:
13});
//buscamos todos los elementos con clase TODO-ItemDeleteIcon.
const deleteIcons =
TestUtils.scryRenderedDOMComponentsWithClass(Todo, 'TODO-
ItemDeleteIcon');
//como solo creamos uno, entonces tomamos este y emulamos el evento
click
TestUtils.Simulate.click(deleteIcons[0]);
//ahora traemos todos los items, no deberia de haber ninguno
const items = TestUtils.scryRenderedDOMComponentsWithClass(Todo,
'TODO-Item');
//como solo creamos uno y borramos uno, el array de items deberia
tener de largo cero
expect(items.length === 0).toBeTruthy();
});

Caso 5: Si presionamos enter para crear un ítem, pero el input esta vacío,
no debería crear elemento alguno
Para este caso verificamos que, luego de haber intentado crear un ítem con el input
vacío, el largo de los ítems sea igual a cero.
it('Si al enviar el formulario este esta vacio no deberia crear la
tarea', () => {
const Todo = TestUtils.renderIntoDocument(<TODO defaultText=""/>);
const input = TestUtils.findRenderedDOMComponentWithTag(Todo,
'input');
TestUtils.Simulate.change(input);
//Simulamos el keDown de la letra #13 de enter
TestUtils.Simulate.keyDown(input, {key: "Enter", keyCode: 13, which:
13});
const items = TestUtils.scryRenderedDOMComponentsWithClass(Todo,
'TODO-Item');
//al crear una tarea, verificamos que el largo sea mayor que cero
expect(items.length === 0).toBeTruthy();
});

Caso 6: La cantidad de ítems debería ser igual que la cantidad de ítems


en el state

Como items es un array en el state de nuestro componente, debemos verificar que


cuando creemos ítems, este cambie.Al encontrar un componente utilizando los
distintos métodos de TestUtils, podremos acceder a este y a todos sus métodos y
atributos. En este caso accederemos a su state.

it('La cantidad de items deberia de ser igual que la cantidad de items


en el state', () => {
const input =
TestUtils.findRenderedDOMComponentWithTag(TodoComponent, 'input');
for (let i = 0; i < 10; i++) {
TestUtils.Simulate.change(input);
//Simulamos el keDown de la letra #13 de enter
TestUtils.Simulate.keyDown(input, {key: "Enter", keyCode: 13,
which: 13});
}
const items =
TestUtils.scryRenderedDOMComponentsWithClass(TodoComponent, 'TODO-
Item');
expect(TodoComponent.state.items.length ===
items.length).toBeTruthy();
});

Вам также может понравиться