Testes unitários com Jest no Node.JS





Uma parte importante do nosso trabalho como desenvolvedor é fazer testes. Durante a construção do nosso código mesmo sem perceber fazemos testes o tempo todo. O grande problema é que quando nosso código vai se tornando grande, com vários arquivos e funções, fica mais difícil testar tudo. Muitas vezes um código que estamos fazendo, pode causar um erro em uma outra parte do código que fizemos antes.

A solução é automatizar nossos testes, podemos fazer isso por meio de um framework. Hoje vou te ensinar a como usar o Jest para fazer seus testes. A configuração do Jest é fácil e vai fazer você ganhar tempo, em segundos toda sua aplicação será testada.


Instalando o Jest


Antes de começar quero lembrar que todos os arquivos deste tutorial, estão disponíveis para download no fim do artigo

Vou iniciar um projeto novo, com o código:



	npm init -y


Com o arquivo "pakage.json" criado vamos a instalação do Jest como dependência.



	npm install jest –save-dev


Compatibilidade com o ES6

Em resumo o ECMAScript 6 ou simplesmente ES6 foi uma atualização do Java Script CJS, lançada em 2015. Tivemos algumas mudanças com a importação / exportação de módulos.

CJS:


    const modulo = require(“modulo”);
  
    module.exports = modulo;


ES6:


    import modulo from “modulo”;
    
    export default modulo;


Jest trabalha com o padrão CJS, então se o seu projeto segue o mesmo padrão, basta configurar o "test" que esta dentro de "scripts", no arquivo "package.json".

    “test”: “node node_modules/jest/bin/jest.js”


Exemplo:




Configurando o Jest para o ES6

Para uso do Jest com ES6, temos disponível um modulo experimental que conseguimos usar sem problemas, pra ser mais exato a documentação diz que ainda podem acontecer bugs, mas como não tive problemas até hoje nos meus testes, vou ensinar as configurações necessárias.


Altere o "package.json" na raiz do seu projeto


    “test”: "node --experimental-vm-modules node_modules/jest/bin/jest.js"


    “type”: “module”,


Segue o exemplo:




Preparando o ambiente.

Vou criar um arquivo chamado math.js, com algumas funções que fazem operações matemáticas simples.


    function soma (a, b) {
        return a + b
    }
    
    function subtracao (a, b) {
        return a - b
    }
    
    function multiplicacao (a, b) {
        return a * b
    }
    
    function divisao (a, b) {
        return a / b
    }
    
    export { soma, subtracao, multiplicacao, divisao }

Nosso objetivo é testar essas funções com o Jest, segundo as boas praticas os arquivos de testes devem ficar dentro de uma pasta chamada “test”. Vamos cria-la.

Dentro da pasta "test" vou criar o arquivo de teste, é comum usar o mesmo nome do arquivo que vamos testar seguido de ".test.js" ou ".spec.js". Meu arquivo vai ficar com o nome "math.test.js".

Lembrando que o Jest vem configurado para reconhecer arquivos que tenham em seu nome "test" ou "spec".


Fazendo o primeiro teste

Importando

A primeira coisa a fazer dentro do arquivo "math.test.js" é importar as funções que vamos testar


    import { soma, subtracao, divisao, multiplicacao } from "../math"


Descrevendo o teste

Logo abaixo da importação vou descrever o meu conjunto de testes.


    import { soma, subtracao, divisao, multiplicacao } from "../math"

    describe('Testando operações matemáticas', () => {


    })


Isto

Vamos descrever as funções individuais, primeiramente a função soma, para isso usados o it, que significa Isto. Vai ficar assim:


    import { soma, subtracao, divisao, multiplicacao } from "../math"

    describe('Testando operações matemáticas', () => {
        it('Deve retornar a soma de dois números', () => {
    
        })
    })


Criando a lógica de teste

Vou usar duas variáveis, a primeira nos da o valor que espero receber e a segunda o valor recebido, comparando as duas variáveis vai ficar claro se o teste foi um sucesso ou não.

Note que na "const recebido", usamos a função "soma" com os números 300 e 200, esperamos que o resultado desta função seja 500. Então colocamos o valor 500 na "const esperado".


    import { soma, subtracao, divisao, multiplicacao } from "../math"

    describe('Testando operações matemáticas', () => {
        it('Deve retornar a soma de dois números', () => {
            const esperado = 500;
            const recebido = soma(300, 200);

        })
    })


Matchers

Queremos que o Jest compare o valor "recebido" com o "esperado". Para fazer isso temos os matchers. Na documentação do Jest temos uma lista com vários tipos de matchers, vale apena consultar a documentação do Jest que nos ajuda bastante por ter a tradução em português.

Vou usar 2 tipos de machers mais comuns:


toBe – compara valores de uma forma superficial.

toEqual – compara não só os valores mais também outras propriedades, chamada de comparação profunda.


Vamos fazer a comparação usando o "expect" e usar o matcher "toBe"


    import { soma, subtracao, divisao, multiplicacao } from "../math"

    describe('Testando operações matemáticas', () => {
        it('Deve retornar a soma de dois números', () => {
        const esperado = 500;
        const recebido = soma(300, 200);

        expect(recebido).toBe(esperado);
        })
    })

Pronto, seu primeiro teste esta configurado!


Rodando o teste

No terminal do VS Code, na pasta raiz do seu projeto digite:


    npm run test


O resultado será este:


PASS test/match.test.js – mostra que o teste passou e que o Jest encontrou nosso arquivo "math.test.js"

Mais abaixo vemos a descrição do nosso teste e o tempo que ele levou: 2 milissegundos.


Criando os testes restantes

Precisamos testar as funções subtração, divisão e multiplicação. Você pode fazer como desafio. Ou copie o código abaixo. Note que também usei o matcher "toEqual" apenas como exemplo.


    import { soma, subtracao, divisao, multiplicacao } from "../math"

    describe('Testando operações matemáticas', () => {
        it('Deve retornar a soma de dois números', () => {
            const esperado = 500;
            const recebido = soma(300, 200);

            expect(recebido).toBe(esperado);
        })

        it('Deve retornar a subtração de dois números', () => {
            const esperado = 500;
            const recebido = subtracao(2000, 1500);

            expect(recebido).toEqual(esperado);
        })

        it('Deve retornar a multiplicação de dois números', () => {
            const esperado = 25;
            const recebido = multiplicacao(5, 5);

            expect(recebido).toBe(esperado);
        })

        it('Deve retornar a divisão de dois números', () => {
            const esperado = 30;
            const recebido = soma(150, 5);

            expect(recebido).toEqual(esperado);
        })
    })


Opções Watch e Coverage

O Jest também traz algumas opções interessantes para incrementar o nosso teste. A documentação do Jest traz muitas outras opções. Vou mostrar como usar 2 delas:

Watch – ele mantêm o teste aberto no terminal e a cada alteração no código, ele faz a atualização do teste em tempo real.

Coverage – cria uma interface HTML com os resultados do teste de uma forma mais atrativa


Fazendo a configuração no package.json

Adicione os scripts e salve o arquivo


CJS:


    "scripts": {
        "test": "node node_modules/jest/bin/jest.js",
        "test:watch": "node node_modules/jest/bin/jest.js --watchAll",
        "test:coverage": "node node_modules/jest/bin/jest.js --coverage"
      },

ES6:


    "scripts": {
        "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
        "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watchAll",
        "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
      },


Rodando teste watch

digite no terminal :


    npm run test:watch


resultado:


Agora cada vez que o código for salvo o teste será atualizado automaticamente, apertando "w" temos acesso ao menu com mais opções e apertando "q" saímos do modo watch.


Rodando teste coverage

digite no terminal:


    npm run test:coverage


resultado:


Nosso teste ganha uma tabela com o nome dos arquivos e a cobertura dos testes em cada arquivo.

No caso 100% do arquivo está sendo coberto pelo nosso teste.


E onde esta a interface HTML?


Na pasta raiz do seu projeto foi criado uma pasta chamada "coverage",

dentro dela temos uma outra pasta "lcov-report"

Dentro dela temos o arquivo "index.html", devemos abrir ele no nosso navegador.



A interface HTML tem o visual mais agradável, um campo de busca e também podemos navegar dentro do código de cada arquivo.

Notamos que temos um campo "Branches" no nosso "coverage", esse "branch" não tem nada a ver com o branch do git. Na verdade a função dele é bem interessante e vale apena explicar neste artigo.


Branch x Estrutura Condicional

Quando falamos de estrutura condicional lembramos imediatamente do "if  else".

E o "branch" tem tudo a ver com essa estrutura. Vamos ver na prática?


Preparando o ambiente

Vou criar um teste que possui um "if else" em seu contexto. Vamos criar na raiz do projeto o arquivo "array.js" e uma função que vai verificar se uma array esta vazia ou não, ela vai retornar "true" ou "false"



    function verificaArrayVazia (array) {
        if (array.length === 0) {
            return true;
        } else {
            return false;
        }
    }
    
    export default verificaArrayVazia;


Criando lógica de teste

Vou criar o arquivo de teste dentro da pasta "test", o nome do arquivo será "array.test.js".

O teste tem uma variável com uma array vazia e vamos testar se a função vai retornar "true".


    import verificaArrayVazia from "../array"

        describe('Testando funções de manipulação de arrays', () => {
            it('Deve verificar se uma array esta vazia ou não', () => {
                const arrayVazia = [];
                const retorno = verificaArrayVazia(arrayVazia);

                expect(retorno).toBe(true);
            })
        })


Rodando o teste coverage

digite no terminal:



  npm run test:coverage


resultado:


Notamos que o teste passou e que foram testados os dois arquivos "array" e "math".

Porem o que mais chama nossa atenção é o "branch" do arquivo "array.js", ele está apenas com 50%, ou seja apenas 50% do codigo está sendo coberto pelo nosso teste.


Por quê?

Como usamos uma estrutura condicional IF, fizemos o teste apenas da metade da função. Faltou testar o ELSE concorda? Então para ter o branch em 100% precisamos adicionar o teste do else a nossa lógica.


Testando o eles

Voltando ao arquivo "array.test.js", adicione o teste do "ELSE" como desafio ou copie o código abaixo.

    import verificaArrayVazia from "../array"

    describe('Testando funções de manipulação de arrays', () => {
        it('Deve verificar se uma array esta vazia ou não', () => {
            const arrayVazia = [];
            const esperado = true;
            const retorno = verificaArrayVazia(arrayVazia);
            expect(retorno).toBe(esperado);
        })

        it('Deve verificar se uma array tem conteúdo', () => {
            const array = ['ola', 'mundo'];
            const esperado = false;
            const retorno = verificaArrayVazia(array);
            expect(retorno).toBe(esperado);
        })
    })


Agora sim nosso "branch" será coberto em 100%.

Vamos refazer o teste "coverage"


    npm run test:coverage


resultado:


Agora deu para entender como o "branch" e as condicionais estão ligadas.

Nem sempre vamos conseguir cobrir nosso código com testes em 100%, o importante mesmo é fazer os testes mais importantes com uma cobertura significativa.

Estou finalizando este artigo por aqui, mais vale lembrar que o assunto é extenso e fizemos testes bem simples.

Provavelmente farei mais artigos sobre testes no futuro. Abrangendo testes mais complexos. Não deixe de consultar a documentação do Jest.


Veja o vídeo no inicio deste artigo para ter mais detalhes.


Baixe os arquivos deste tutorial no GitHub.


E até a próxima!











Comentários

Postagens mais visitadas deste blog

EsLint no Visual Studio Code - Tutorial Rápido!

CSS Flex box e Grid - Saiba de uma vez por todas, como alinhar e posicionar elementos no HTML.