Testando código JavaScript com o Jest para manequins. Parte 1

Olá Habr! Este guia é a primeira parte de uma série planejada de artigos sobre uma estrutura de teste tão boa como o Jest. O material será útil para iniciantes e para aqueles que estão começando a se familiarizar com os testes e gostaria de estudar essa estrutura. Na primeira parte, discutiremos: como iniciar o gracejo, como escrever um teste simples e quais métodos existem para comparar os valores a serem verificados com os esperados. Quem se importa - bem-vindo ao kat!

O que é Jest?


Conforme indicado na página inicial do projeto:
O Jest é um incrível ambiente de teste de JavaScript com foco na simplicidade.
E, de fato, Jest é muito simples. Não requer configurações adicionais, fácil de entender e usar, e também possui uma documentação muito boa. Ótimo para projetos que usam Nó, Reagir, Angular, Vue, Babel, TypeScript e muito mais.
Também é de código aberto e suportado pelo Facebook.

Instalação


Para instalar o Jest em seu projeto, faça:

npm install --save-dev jest

Se você estiver usando fio:

yarn add --dev jest

Após a instalação, você pode atualizar a seção de scripts do seu package.json:

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

Com esta simples chamada, já podemos executar nossos testes (na verdade, o gracejo exigirá pelo menos um teste para existir).

Você também pode instalar globalmente (mas eu não recomendaria isso, pois para mim a instalação global de módulos é uma prática recomendada):

npm install jest --global

E de acordo com o fio:

yarn global add jest

Depois disso, você pode usar o gracejo diretamente da linha de comando.

Ao chamar o comando jest --init na raiz do projeto, depois de responder a algumas perguntas, você obterá o arquivo de configurações jest.config.js. Ou você pode adicionar a configuração diretamente ao seu package.json. Para fazer isso, adicione a chave "jest" à raiz json e, no objeto correspondente, você pode adicionar as configurações necessárias. Analisaremos as opções em si mais tarde. Isso não é necessário neste estágio, pois o gracejo pode ser usado imediatamente, sem configurações adicionais.

Primeiro teste


Vamos criar o arquivo first.test.js e escrever nosso primeiro teste:

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

E execute nossos testes usando o npm run test ou diretamente com o comando jest (se instalado globalmente). Após o lançamento, veremos um relatório sobre a aprovação nos testes.

 <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

Vamos terminar nosso teste e executar o gracejo novamente:

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

Como podemos ver, agora nosso teste não passa no teste. O Jest exibe informações detalhadas sobre onde o problema ocorreu, qual foi o resultado esperado e o que obtivemos.

Agora vamos analisar o código do teste em si. Função de testeusado para criar um novo teste. São necessários três argumentos (no exemplo, usamos uma chamada com dois argumentos). A primeira é uma linha com o nome do teste, sua brincadeira será exibida no relatório. A segunda é uma função que contém a lógica do nosso teste. Você também pode usar o terceiro argumento - tempo limite. É opcional e seu valor padrão é de 5 segundos. É definido em milissegundos. Este parâmetro é necessário quando trabalhamos com código assíncrono e retornamos uma promessa da função de teste. Indica quanto tempo a brincadeira deve esperar a promessa se resolver. Após esse período, se a promessa não for permitida - o gracejo considerará que o teste falhou. Mais informações sobre como trabalhar com chamadas assíncronas estão nas seguintes partes. Como alternativa, você pode usá- lo () em vez de test ().. Não há diferença entre essas chamadas. it () é apenas um alias na função test () .

Dentro da função de teste, chamamos primeiro de expect () . Para ele, passamos o valor que queremos verificar. No nosso caso, este é o resultado da chamada Math.max (1, 5, 10) . expect () retorna um objeto de wrapper que possui vários métodos para comparar o valor resultante com o esperado. Um desses métodos que usamos é toBe .

Vejamos o principal desses 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 () - usado quando é necessário verificar a exceção. Você pode verificar o fato do erro em si e verificar se há exceções de lançamento de uma determinada classe, pela mensagem de erro ou pela correspondência da mensagem com a expressão regular.

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

  • not - essa propriedade permite verificar a desigualdade. Ele fornece um objeto que possui todos os métodos listados acima, mas eles funcionarão ao contrário.

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

Vamos escrever alguns testes simples. Primeiro, crie um módulo simples que contenha vários métodos para trabalhar com círculos.

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

Em seguida, adicione os testes:

// 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();
});

Nestes testes, verificamos o resultado de 2 métodos - área e circunferência . Usando o método toBeCloseTo, verificamos o resultado esperado. No primeiro caso, verificamos ou a área calculada do círculo com um raio de 5 é aproximadamente igual a 78,54, enquanto a diferença com o valor obtido (será 78,53981633974483) não é grande e o teste será contado. No segundo, indicamos que estamos interessados ​​em verificar com uma casa decimal. Também chamamos nossos métodos sem argumentos e verificamos o resultado usando toBeNaN . Como o resultado de sua execução será NaN, os testes serão aprovados com sucesso.

Vamos dar outro exemplo. Vamos criar uma função que irá filtrar uma variedade de produtos por preço:

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

E adicione um teste:

// 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 });
});

Neste teste, verificaremos o resultado da função byRangePrice . Primeiro, verificamos a correspondência do comprimento da matriz resultante com a esperada - 2. A próxima verificação exige que o elemento contenha o elemento - {name: 'tomato', price: 26}. O objeto na matriz e o objeto passado paraContainEqual são dois objetos diferentes, não uma referência ao mesmo. Mas toContainEqual comparará cada propriedade. Como os dois objetos são idênticos, a verificação será bem-sucedida. Em seguida, usamos toEqual para verificar a estrutura de toda a matriz e seus elementos. Os toBeGreaterThanOrEqual e métodos toBeLessThanOrEqual vai nos ajudar a verificar o preço do primeiro e do segundo elemento da matriz. E, finalmente, chamando not.toContainEqualirá verificar se o elemento está contido na matriz - {name: 'orange', price: 38}, que, por condição, não deve estar lá.

Nestes exemplos, escrevemos alguns testes simples usando as funções de verificação descritas acima. Nas partes a seguir, discutiremos o trabalho com código assíncrono, as funções de gracejo que não foram abordadas nesta parte do tutorial, falaremos sobre sua configuração e muito mais.

All Articles