Prueba de código JavaScript con Jest para tontos. Parte 1

Hola Habr! Esta guía es la primera parte de una serie planificada de artículos sobre un marco de prueba tan bueno como Jest. El material será útil para principiantes y para aquellos que recién se están familiarizando con las pruebas, y les gustaría estudiar este marco. En la primera parte analizaremos: cómo comenzar con broma, cómo escribir una prueba simple y qué métodos existen para comparar los valores que se deben comparar con los esperados. ¿A quién le importa? ¡Bienvenido a Kat!

¿Qué es broma?


Como se indica en la página de inicio del proyecto:
Jest es un increíble entorno de prueba de JavaScript con un enfoque en la simplicidad.
Y de hecho, Jest es muy simple. No requiere configuraciones adicionales, fácil de entender y usar, y también tiene bastante buena documentación. Ideal para proyectos con Node, React, Angular, Vue, Babel, TypeScript y más.
También es de código abierto y es compatible con Facebook.

Instalación


Para instalar Jest en su proyecto, haga:

npm install --save-dev jest

Si está utilizando hilo:

yarn add --dev jest

Después de la instalación, puede actualizar la sección de scripts de su package.json:

“scripts” : {
     “test”: “jest”
}

Con esta simple llamada, ya podemos ejecutar nuestras pruebas (de hecho, jest requerirá al menos una prueba para existir).

También puede instalarlo globalmente (pero no recomendaría hacerlo, ya que para mí la instalación global de módulos es una mala práctica):

npm install jest --global

Y en consecuencia para hilo:

yarn global add jest

Después de eso, puede usar jest directamente desde la línea de comandos.

Al llamar al comando jest --init en la raíz del proyecto, después de responder algunas preguntas, obtendrá el archivo de configuración jest.config.js. O puede agregar la configuración directamente a su package.json. Para hacer esto, agregue la clave "jest" a la raíz json y en el objeto correspondiente puede agregar la configuración que necesita. Analizaremos las opciones ellos mismos más tarde. Esto no es necesario en esta etapa, ya que jest se puede usar de inmediato, sin configuraciones adicionales.

Primer examen


Creemos el archivo first.test.js y escribamos nuestra primera prueba:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(10);
});

Y ejecute nuestras pruebas usando npm run test o directamente con el comando jest (si está instalado globalmente). Después del lanzamiento, veremos un informe sobre cómo pasar las pruebas.

 <b>PASS</b>  ./first.test.js
  ✓ My first test (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.618 s, estimated 1 s

Rompamos nuestra prueba y corramos broma nuevamente:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(5);
});

Como podemos ver, ahora nuestra prueba no pasa la prueba. Jest muestra información detallada sobre dónde ocurrió el problema, cuál fue el resultado esperado y qué obtuvimos en su lugar.

Ahora analicemos el código de la prueba en sí. Función de pruebausado para crear una nueva prueba. Se necesitan tres argumentos (en el ejemplo usamos una llamada con dos argumentos). La primera es una línea con el nombre de la prueba, su broma se mostrará en el informe. La segunda es una función que contiene la lógica de nuestra prueba. También puede usar el tercer argumento: tiempo de espera. Es opcional y su valor predeterminado es de 5 segundos. Se establece en milisegundos. Este parámetro es necesario cuando trabajamos con código asincrónico y devolvemos una promesa de la función de prueba. Indica cuánto tiempo debe esperar la broma para que se resuelva la promesa. Después de este tiempo, si la promesa no fue permitida, broma considerará que la prueba falló. En las siguientes partes encontrará más información sobre cómo trabajar con llamadas asincrónicas. Alternativamente, puede usarlo () en lugar de test ().. No hay diferencia entre tales llamadas. it () es solo un alias en la función test () .

Dentro de la función de prueba, primero llamamos a wait () . A él le pasamos el valor que queremos verificar. En nuestro caso, este es el resultado de llamar a Math.max (1, 5, 10) . expect () devuelve un objeto contenedor que tiene varios métodos para comparar el valor resultante con el esperado. Uno de estos métodos que utilizamos es toBe .

Veamos el principal de estos métodos:

  • toBe() — , , . Object.is(). === 0 -0, NaN c NaN.
  • toEqual() — , . . . .

    test('toEqual with objects', () => {
        expect({ foo: 'foo', subObject: { baz: 'baz' } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  // 
        expect({ foo: 'foo', subObject: { num: 0 } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  //    .
    });
    
    test('toEqual with arrays', () => {
        expect([11, 19, 5]).toEqual([11, 19, 5]); // 
        expect([11, 19, 5]).toEqual([11, 19]); // 
    });
    

  • toContain() — . ===.

    const arr = ['apple', 'orange', 'banana'];
    expect(arr).toContain('banana');
    expect(new Set(arr)).toContain('banana');
    expect('apple, orange, banana').toContain('banana');
    

  • toContainEqual() — .

    expect([{a: 1}, {b: 2}]).toContainEqual({a: 1});
    

  • toHaveLength() — length .

    expect([1, 2, 3, 4]).toHaveLength(4);
    expect('foo').toHaveLength(3);
    expect({ length: 1 }).toHaveLength(1);
    

  • toBeNull() — null.
  • toBeUndefined() — undefined.
  • toBeDefined()toBeUndefined. !== undefined.
  • toBeTruthy() — true. false, null, undefined, 0, NaN .
  • toBeFalsy()toBeTruthy(). false.
  • toBeGreaterThan() toBeGreaterThanOrEqual() — , >, >=.
  • toBeLessThan() toBeLessThanOrEqual()toBeGreaterThan() toBeGreaterThanOrEqual()
  • toBeCloseTo() — , , . .

    const num = 0.1 + 0.2; // 0.30000000000000004
    expect(num).toBeCloseTo(0.3);
    expect(Math.PI).toBeCloseTo(3.14, 2);
    

  • toMatch() — .

    expect('Banana').toMatch(/Ba/);
    

  • toThrow () : se usa cuando es necesario verificar la excepción. Puede verificar tanto el hecho del error en sí como las excepciones de una determinada clase, ya sea por el mensaje de error o por la correspondencia del mensaje con la expresión regular.

    function funcWithError() {
        throw new Error('some error');
    }   
    expect(funcWithError).toThrow();
    expect(funcWithError).toThrow(Error);
    expect(funcWithError).toThrow('some error');
    expect(funcWithError).toThrow(/some/);
    

  • no : esta propiedad le permite verificar la desigualdad. Proporciona un objeto que tiene todos los métodos enumerados anteriormente, pero funcionarán al revés.

    expect(true).not.toBe(false);
    expect({ foo: 'bar' }).not.toEqual({});
    
    function funcWithoutError() {}
    expect(funcWithoutError).not.toThrow();
    

Escribamos un par de pruebas simples. Primero, cree un módulo simple que contendrá varios métodos para trabajar con círculos.

// src/circle.js
const area = (radius) => Math.PI * radius ** 2;
const circumference = (radius) => 2 * Math.PI * radius;
module.exports = { area, circumference };

A continuación, agregue las pruebas:

// tests/circle.test.js
const circle = require('../src/circle');

test('Circle area', () => {
    expect(circle.area(5)).toBeCloseTo(78.54);
    expect(circle.area()).toBeNaN();
});

test('Circumference', () => {
    expect(circle.circumference(11)).toBeCloseTo(69.1, 1);
    expect(circle.circumference()).toBeNaN();
});

En estas pruebas, verificamos el resultado de 2 métodos: área y circunferencia . Usando el método toBeCloseTo, verificamos el resultado esperado. En el primer caso, verificamos o el área calculada del círculo con un radio de 5 es aproximadamente igual a 78.54, mientras que la diferencia con el valor obtenido (será 78.53981633974483) no es grande y se contará la prueba. En el segundo, indicamos que estamos interesados ​​en verificar hasta 1 lugar decimal. También llamamos a nuestros métodos sin argumentos y verificamos el resultado usando toBeNaN . Como el resultado de su ejecución será NaN, las pruebas se pasarán con éxito.

Tomemos otro ejemplo. Creemos una función que filtre una variedad de productos por precio:

// src/productFilter.js
const byPriceRange = (products, min, max) =>
         products.filter(item => item.price >= min && item.price <= max);
module.exports = { byPriceRange };

Y agregue una prueba:

// tests/product.test.js
const productFilter = require('../src/producFilter');

const products = [
    { name: 'onion', price: 12 },
    { name: 'tomato', price: 26 },
    { name: 'banana', price: 29 },
    { name: 'orange', price: 38 }
];

test('Test product filter by range', () => {
    const FROM = 15;
    const TO = 30;
    const filteredProducts = productFilter.byPriceRange(products, FROM, TO);

    expect(filteredProducts).toHaveLength(2);
    expect(filteredProducts).toContainEqual({ name: 'tomato', price: 26 });
    expect(filteredProducts).toEqual([{ name: 'tomato', price: 26 }, { name: 'banana', price: 29 }]);
    expect(filteredProducts[0].price).toBeGreaterThanOrEqual(FROM);
    expect(filteredProducts[1].price).toBeLessThanOrEqual(TO);
    expect(filteredProducts).not.toContainEqual({ name: 'orange', price: 38 });
});

En esta prueba, verificaremos el resultado de la función byRangePrice . Primero, verificamos la correspondencia de la longitud de la matriz resultante con la esperada - 2. La siguiente verificación requiere que el elemento contenga el elemento - {nombre: 'tomate', precio: 26}. El objeto en la matriz y el objeto pasado aContainEqual son dos objetos diferentes, no una referencia al mismo. Pero toContainEqual comparará cada propiedad. Como ambos objetos son idénticos, la verificación será exitosa. A continuación, usamos toEqual para verificar la estructura de toda la matriz y sus elementos. Los métodos toBeGreaterThanOrEqual y toBeLessThanOrEqual nos ayudarán a verificar el precio del primer y segundo elemento de la matriz. Y finalmente, llamando a not.toContainEqualcomprobará si el elemento está contenido en la matriz - {nombre: 'naranja', precio: 38}, que, por condición, no debería estar allí.

En estos ejemplos, escribimos algunas pruebas simples utilizando las funciones de verificación descritas anteriormente. En las siguientes partes, discutiremos el trabajo con código asíncrono, las funciones de broma que no se cubrieron en esta parte del tutorial, hablaremos sobre su configuración y mucho más.

All Articles