Skip to main content

Como escrever seu primeiro teste unitário em JavaScript

Escrito por

Raphael Mun

feature java dto

28 de outubro de 2022

0 minutos de leitura

Testar o código é o primeiro passo para torná-lo seguro. Uma das melhores maneiras de fazer isso é usar testes unitários, garantindo que cada uma das funções menores de um aplicativo se comporte como esperado — especialmente quando o aplicativo recebe entradas incomuns, inválidas ou potencialmente prejudiciais.

Por que fazer testes unitários?

Há várias abordagens para testes unitários. Entre os principais objetivos desses testes estão:

  • Verificar funcionalidades: os testes unitários garantem que o código faça o que deve fazer — e não faça o que não deve, origem da maioria dos bugs.

  • Prevenir regressões no código: quando encontramos um bug, adicionar um teste unitário para verificar aquele cenário impede que alterações no código façam o bug voltar no futuro.

  • Documentar o código: com os testes unitários certos, um conjunto completo de testes e resultados especifica como o aplicativo deve funcionar.

  • Proteger seu aplicativo: os testes unitários podem verificar se há vulnerabilidades exploráveis, como as que permitem uma injeção de SQL maliciosa.

Definição do escopo e escrita de testes unitários

Um framework de testes unitários permite escrever e automatizar testes com rapidez, além de integrá-los aos processos de desenvolvimento e implantação. Esses frameworks costumam oferecer suporte a testes tanto no front-end quanto no back-end de código JavaScript.

Confira algumas orientações gerais para escrever testes unitários de desempenho e criar código que possa ser testado.

Mantenha os testes unitários curtos e simples

Não sobrecarregue os testes unitários. Cada teste deve ter apenas algumas linhas de código e verificar partes pequenas e funcionais do aplicativo.

Considere casos de teste positivos e negativos

É útil escrever um teste para confirmar que uma função é executada corretamente. No entanto, é muito mais eficaz criar um conjunto mais amplo de testes para verificar se a função falha como deveria quando é usada incorretamente ou em casos extremos. Esses testes negativos podem ser ainda mais valiosos, pois ajudam a prever situações inesperadas, como quando uma função deve gerar uma exceção ou como deve lidar com dados malformados.

Divida funções longas e complexas

Funções grandes, com muita lógica, são difíceis de testar. Quando há operações demais, fica difícil testar cada variável de forma eficaz. Se uma função for complexa demais, divida-a em funções menores para testar cada uma separadamente.

Evite conexões de rede e de banco de dados

Os testes unitários devem ser rápidos e leves, mas funções que fazem chamadas de rede ou se conectam a outros aplicativos ou processos demoram para ser executadas. Isso dificulta executar muitas operações ao mesmo tempo e pode deixar o código mais frágil. Você pode usar simulações em testes unitários para simular chamadas de rede ou de banco de dados — assim, é possível testar o restante da função. As conexões reais de rede e de banco de dados podem ser incluídas em outro processo de teste, chamado teste de integração.

Como escrever testes unitários

Agora que vimos algumas boas práticas de testes unitários, você está pronto para escrever seu primeiro teste unitário em JavaScript.

Este tutorial usa o framework Mocha, um dos mais populares para testes unitários. Cada framework de testes tem suas particularidades, mas eles são parecidos o suficiente para que, ao aprender os conceitos básicos, você consiga alternar entre eles sem dificuldade.

Para acompanhar esta demonstração, instale o Node.js no seu computador.

Criar um novo projeto

Comece abrindo uma janela do terminal ou do prompt de comando na pasta de um novo projeto. Em seguida, crie nela um projeto Node.js digitando npm init -y.

Isso cria um arquivo package.json na pasta, permitindo instalar o Mocha como dependência de desenvolvimento usando npm install -D mocha.

Em seguida, abra o arquivo package.json no editor de código e substitua o script de teste temporário por mocha:

  "scripts": {
    "test": "mocha"
  },

Implementar uma classe

Agora, vamos criar um sistema simples de semáforo para testar.

No diretório do projeto, crie um arquivo chamado traffic.js e adicione o código a seguir para criar uma classe TrafficLight:

class TrafficLight {
    constructor() {
        this.lightIndex = 0;
    }

    static get colors() {
        return [ "green", "yellow", "red" ];
    }
    get light() {
        return TrafficLight.colors[ this.lightIndex ];
    }
    next() {
        this.lightIndex++;
        // This is intentionally wrong!
        if( this.lightIndex > TrafficLight.colors.length ) {
            this.lightIndex = 0;
        }
    }
}

module.exports = TrafficLight;

Esta classe tem quatro partes:

  • TrafficLight.colors: uma propriedade constante com as cores do semáforo.

  • lightIndex: uma variável que acompanha o índice da cor atual do semáforo.

  • light: uma propriedade da classe que retorna a cor atual do semáforo como uma string.

  • next(): uma função que muda o semáforo para a próxima cor.

Configurar e adicionar nosso primeiro teste unitário

Agora é hora de adicionar testes unitários ao código.

Crie uma pasta chamada test no diretório do projeto. Por padrão, é nela que o Mocha procura os testes unitários. Em seguida, crie um arquivo chamado traffic.test.js dentro da nova pasta de testes.

Em seguida, importe a classe TrafficLight no início do arquivo:

const TrafficLight = require( "../traffic" );

Também vamos usar o módulo assert nos testes, então inclua-o no código:

const assert = require( "assert" );

Com o Mocha, podemos agrupar testes unitários usando a função describe(). Assim, podemos criar um grupo de nível superior para esta classe:

describe( "TrafficLight", function () {
});

Em seguida, vamos criar alguns testes unitários para validar as cores do semáforo em um subgrupo chamado colors, dentro do grupo TrafficLight:

describe( "TrafficLight", function () {
    describe( "colors", function () {
    });
});

Neste primeiro teste unitário, podemos verificar se colors tem apenas três estados: verde, amarelo e vermelho. O teste é definido com uma função it() dentro do grupo describe(). Escreva o teste assim:

describe( "TrafficLight", function () {
    describe( "colors", function () {
        it( "has 3 states", function () {
            const traffic = new TrafficLight();
            assert.equal( 3, TrafficLight.colors.length );
        });
    });
});

Agora, vamos executar o teste unitário e ver se ele passa.

Execute npm test na janela do terminal. Se estiver tudo certo, o Mocha exibirá os resultados da execução do teste unitário.

Saída do terminal mostrando testes do Mocha para as cores do semáforo, com um teste aprovado em 8 ms.

Adicionar mais testes unitários

Nosso projeto já está pronto para executar testes unitários. Podemos adicionar mais testes para garantir que o código esteja funcionando corretamente.

Primeiro, adicione um teste unitário ao grupo colors para validar se as cores do semáforo estão corretas e na ordem certa. Veja uma maneira de implementar esse teste:

it( "colors are in order", function () {
    const expectedLightOrder = [ "green", "yellow", "red" ];
    const traffic = new TrafficLight();
    for( let i = 0; i < expectedLightOrder.length; i++ ) {
        assert.equal( expectedLightOrder[ i ], TrafficLight.colors[ i ] );
    }
});

Em seguida, teste a função next() para verificar se ela muda corretamente as cores do semáforo. Crie um novo subgrupo e adicione dois testes unitários: um para verificar se as cores mudam na ordem correta e outro para conferir se, depois de red, o ciclo volta para green.

Escreva os testes unitários assim:

describe( "next()", function () {
    it( "changes lights in order", function () {
        const traffic = new TrafficLight();
        for( let i = 0; i < TrafficLight.colors.length; i++ ) 
            assert.equal( traffic.light, TrafficLight.colors[ i ] );
            traffic.next();
        }
    });
    it( "loops back to green", function () {
        const traffic = new TrafficLight();
        // Change the light 3x to go from green -> yellow -> red -> green
        for( let i = 0; i < 3; i++ ) {
            traffic.next();
        }
        assert.equal( traffic.light, TrafficLight.colors[ 0 ] );
    });
});

Ao executar os testes novamente, veremos que um deles falha. Isso acontece porque há um bug na classe TrafficLight.

Saída do terminal de testes em JavaScript com Mocha, mostrando três testes aprovados e uma falha na verificação do semáforo.

Corrigir o bug

Abra novamente o código da classe TrafficLight e localize o comentário dentro da função next() que diz // This is intentionally wrong!

Com nossos testes unitários, sabemos que essa função não está voltando corretamente para green. O código verifica se o valor de lightIndex ultrapassa o número de cores do semáforo, mas o índice começa em 0. Em vez disso, devemos voltar ao início assim que o valor chegar ao número exato de cores. Vamos atualizar o código para redefinir o valor de lightIndex para 0 quando ele for igual ao tamanho da lista de cores do semáforo:

// This is intentionally wrong!
if( this.lightIndex === TrafficLight.colors.length ) {
    this.lightIndex = 0;
}

Agora, todos os seus testes unitários devem passar. E o melhor: mesmo que a classe TrafficLight seja refatorada ou bastante modificada, nossos testes unitários vão detectar esse bug antes que ele chegue aos usuários.

Saída do terminal mostrando testes unitários do Mocha para as cores de TrafficLight, com quatro testes aprovados.

Para concluir

Os testes unitários são fáceis de configurar e uma ferramenta eficaz no desenvolvimento de software. Eles ajudam a eliminar bugs logo no início e evitam que voltem. Assim, os projetos ficam mais fáceis de gerenciar e manter, mesmo quando crescem e se tornam mais complexos — especialmente em equipes maiores de desenvolvimento. Testes automatizados como esses também permitem que desenvolvedores refatorem e otimizem o código sem se preocupar se o novo código está funcionando corretamente.

Os testes unitários são uma parte essencial do seu processo de desenvolvimento e são fundamentais para ajudar você a criar aplicativos JavaScript melhores e mais seguros.

Bons testes!

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Leia mais

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

illustration hero ai
Blog

O furacão da IA chegou

A IA está acelerando tanto a criação de software quanto os ataques cibernéticos. As lideranças devem proteger agentes e código desde o início, aplicar controles em tempo de execução e validar as defesas de forma independente.

feature insights context
Blog

A prevenção é essencialmente um problema resolvido?

A prevenção em código gerado por agentes está resolvida do ponto de vista arquitetural — mas escolher controles que protejam a segurança sem desacelerar o desenvolvimento continua sendo um desafio.