Executando verificação de segurança...
12

Test runners e frameworks de teste em JavaScript

Pessoal, eu estava assistindo uma sequência de aulas sobre testes no curso.dev e as coisas ditas lá me incentivaram a criar esta publicação sobre test runners (ou framework de testes). Vou passar uma visão geral que contará um pouco da história e minha experiência com eles.

Considero que essa publicação possa ser especialmente boa para quem está começando nos testes em JavaScript, mas vale a pena ler para conhecer as opções e ver como elas funcionam.

Test runner ou Framework de teste?

Não é muito comum ouvir esses termos ao trabalhar com JavaScript porque não existem muitas discussões sobre testes. Geralmente o projeto inicia com as bibliotecas que a equipe já está acostumada, porque as mais populares tem um bom suporte e não dão dor de cabeça.

Para executar testes precisamos executar o código a ser testado, realizar asserções para comparar se ocorreu o que deveria, ver os resultados dos testes, ver a cobertura dos testes, criar mocks, depurar etc.

Então, simplificando, vamos considerar que um runner apenas executas os testes, e um framework de teste acaba incluindo tudo o que você precisa, mas você pode escolher ferramentas diferentes para realizar cada tarefa, ou ainda misturar um framework para executar os testes, com outra biblioteca para realizar asserções, por exemplo.

Claro, na prática, o ideal é simplificar o máximo possível para depender de menos peças. Então, vamos conhecer algumas opções.

Mocha

O Mocha teve seu primeiro commit em 2011 e continua sendo atualizado. É bastante utilizado, com mais de 7 milhões de downloads semanais no NPM, e eu acho que nunca usei ele.

Eu quase não vejo alguém falando dele, mas como eu disse antes, é possível que você encontre uma equipe que esteja acostumada com ele e não exista motivos para trocar.

Se você for no site do Mocha, verá que ele se considera um framework de testes, e executa os testes em forma serial, mas passou a suportar testes em paralelo na versão 8.0.0, lançada em 2020.

Apesar dele se considerar um framework de testes, não possui asserções. Na sua documentação, tem o seguinte código de exemplo, que utiliza o assert:

var assert = require('assert');
describe('Array', function () {
  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

O código acima usa funções describe e it, que são funções do próprio Mocha.

Um dos pontos que pode influenciar na escolha de um test runner (ou framework) é como ele exibe o retorno, tanto de sucesso quanto de erro. Alguns são muito verbosos, outros são mais concisos e fáceis de entender. Veja dois exemplos que obtive da própria documentação:

Os testes passaram

Um teste não passou

Esses exemplos usaram o reporter "spec", mas o Mocha também disponibiliza vários outros reporters, visite o link da documentação para ver alguns exemplos.

Ava

O Ava teve seu primeiro commit em 2014 e também continua sendo atualizado. É mais novo do que o Mocha, mas já suportava testes em paralelo anos antes do Mocha. Hoje, não é uma escolha popular, com 200 mil a 300 mil downloads semanais no NPM, e a primeira vez que ouvi falar do Ava foi no curso.dev ao mencionar a história com o Mocha.

Apesar de se considerar um test runner, o Ava já possui asserções. Veja um exemplo de teste que peguei na própria documentação:

import test from 'ava';

test('foo', t => {
	t.pass();
});

test('bar', async t => {
	const bar = Promise.resolve('bar');
	t.is(await bar, 'bar');
});

E exemplos de resultados de testes que encontrei no freeCodeCamp:

Os testes passaram

Um teste não passou

O artigo é de novembro de 2017, então a forma como os resultados são exibidos pode ter mudado.

Para obter um relatório de cobertura de testes, o artigo do freeCodeCamp utilizou outra biblioteca, nyc, e a documentação do Ava indicou a biblioteca c8.

Jasmine

Jasmine teve seu primeiro commit em 2008; é mais antigo do que os outros mencionados. Possui cerca de 1.3 milhão de downloads semanais no NPM, e commits menos frequentes, mas também tem menos issues e PRs abertos. O Jasmine também suporta testes em paralelo.

O site oficial tem o seguinte teste de exemplo:

describe("A suite is just a function", function() {
  let a;

  it("and so is a spec", function() {
    a = true;

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

O uso de describe e it pode lembrar o Mocha, mas pelo Jasmine ser mais antigo, talvez na verdade o Mocha que lembre o Jasmine. A única diferença no código entre os dois exemplos são as asserções, e como é possível utilizar outras bibliotecas de asserções, já podemos deduzir que é bem simples trocar um test runner (ou framework) por outro, se eles possuírem a mesma interface para as asserções, ou se você não trocar a biblioteca de asserção.

Esses são exemplos de resultados de testes que encontrei no freeCodeCamp:

Os testes passaram

Um teste não passou

O artigo também é antigo, de dezembro de 2018, então a forma como os resultados são exibidos pode ter mudado.

Jest

O Jest foi criado em 2011 pelo Facebook e transferido para a OpenJS Foundation em 2022. Hoje, possui em torno de 22 milhões de downloads semanais no NPM. Se considera um framework de testes e é, sem dúvidas, o mais popular hoje.

Foi o primeiro framework de testes que eu tive contato, e o que mais usei desde então. Possui tudo incluso: o runner, asserções, relatório de resultados, de cobertura, suporte a testes em parelelo etc. O TabNews, inclusive, começou usando o Jest em 2021 no PR #76.

Veja um exemplo de teste da própria documentação:

const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Lembra muito o Jasmine. O Jest também tem suporte ao describe e it do exemplo do Jasmine, e não sei se ambos possuem todas as mesmas asserções, mas os dois exemplos funcionariam com os dois frameworks.

Agora, exemplos de resultados de sucesso e erro:

Os testes passaram

Um teste não passou

Vitest

O Vitest teve seu primeiro commit em dezembro de 2021, e apesar de ser novo, ele possui 5 milhões de downloads semanais no NPM e está com um crescimento forte. Ele se considera um framework de testes e é concorrente direto do Jest.

Talvez você ache estranho ele ser tão recente e tão popular, mas houve uma época, por volta de 2019 e 2021, onde muitos frameworks de JavaScript surgiam fazendo algo que outros já faziam, porém divulgando-se como "mais rápido". Esse problema provavelmente sempre existiu, mas nessa época, além de ter sido algo mais intenso, havia uma ampla divulgação e experimentação desses projetos.

O Vitest surgiu "no embalo" do Vite, uma ferramenta de desenvolvimento que hoje é usada por padrão em templates de Vue e React. Pelos projetos estarem relacionados, e pelo Vite ser uma ferramenta realmente boa na época, que disputou de frente com o Webpack, o Vitest acabou se destacando bastante em pouco tempo.

É muito simples trocar o Jest pelo Vitest, e o ErickCReis fez isso no TabNews no PR #1642, onde o tempo de execução do CI no GitHub caiu de aproximadamente 4 minutos para 2 minutos e 20 segundos.

Veja um exemplo de teste obtido na documentação:

import { expect, test } from 'vitest'
import { sum } from './sum.js'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

E exemplos de sucesso e erro, na base de código do TabNews:

Testes com sucesso

Um teste com erro

Node e Bun test runner

Se você está trabalhando em um projeto que utiliza o Node.js ou o Bun, pode usar os runners de cada um deles.

O test runner do Node.js é recente, surgiu na versão 18 em 2022, e foi adicionando algumas facilidades nas versões mais recentes, como suporte ao mock de datas e glob para identificar os arquivos a serem testados. Se usar as próprias asserções do Node.js, dependendo do projeto, pode não precisar de nenhuma outra dependência.

Eu já usei num projeto pequeno, achei os relatórios verbosos, e recebia alguns warnings por usar features experimentais. Veja um exemplo:

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';

import { Cat } from './Cat.js';
import { Fish } from './Fish.js';
import { Plastic } from './Plastic.js';

describe('Cat', () => {
  it('should eat fish', () => {
    const cat = new Cat();
    const fish = new Fish();

    assert.doesNotThrow(() => cat.eat(fish));
  });
});

E exemplo de resultado que encontrei no site Sonar Source:

1 sucesso e 1 erro

O próprio Bun é recente, lançado em 2021, com a versão 1.0 em 2023, mas ele também inclui um test runner. Estou usando num projeto pequeno em TypeScript, me agradou. Simples, não precisei configurar nada, e os asserts são parecidos com o que já vimos no Jasmine, Jest e Vitest:

import { test, expect, describe } from "bun:test";

describe("math", () => {
  test("add", () => {
    expect(2 + 2).toEqual(4);
  });

  test("multiply", () => {
    expect(2 * 2).toEqual(4);
  });
});

E um exemplo de resultado:

3 sucessos e 1 erro

Qual escolher?

Existem vários outros tests runners e frameworks que eu não citei e também não utilizei, como o Karma, Japa e QUnit. Apesar disso, você não precisa conhecer todas as opções para escolher uma.

Por exemplo, eu nunca tive problema com Jest, e provavelmente é o que eu recomendaria para um "testador de primeira viagem". É o mais utilizado hoje, tem amplo suporte, com provavelmente tudo o que você precisará.

Se você já escreveu testes em alguns projetos, provavelmente tem a capacidade de escolher sozinho. Eu não vejo a necessidade de trocar um test runner em um projeto, caso o atual esteja servindo bem, a não ser que seja uma troca muito simples e traga benefícios de velocidade, por exemplo, como mencionado no caso do TabNews ao trocar Jest por Vitest. Lembre-se que não é garantido que em todas situações essa troca fará sentido.

Se você for criar um projeto pequeno, pode ter a liberdade de escolher um test runner mais simples que sabe que atenderá as demandas do projeto. Por exemplo se o projeto é em Node, experimentar o test runner do Node para ver se te atende.

Num projeto maior, recomendo ir pelas escolhas mais seguras para não precisar trocar no meio do caminho. O que é uma escolha segura, depende da sua experiência. Para mim, seria Jest ou Vitest, por serem os frameworks que mais utilizo no dia a dia. Se você usa TypeScript, pode preferir um framework que dê um suporte mais simples, com menos configurações, como é o caso do Vitest, quando comparado ao Jest.

Como curiosidade, veja a popularidade no GitHub, medida em estrelas, de cada projeto mencionado (com exceção dos test runners do Node.js e Bun):

Gráfico de estrelas de cada projeto

Carregando publicação patrocinada...
3

show demais, parabens pelo artigo!!
eu so acrescentaria mais um:
Poku

ja ouviu falar dele ?
https://poku.io/

vale a pena dar uma chance, super facil de aprender, open-source, muito organizado, e criado e mantido por um brasileiro.

vale a pena dar uma olhada e valorizar o que fazemos "dentro de casa".

sem contar na qualidade excepcional que ele tem.

1
1

Sim, não mencionei eles pois são de uma outra categoria de testes, assim como não mencionei como os frameworks de teste podem emular o DOM para realizar testes em componentes feitos para web, e bibliotecas complementares que possibilitam esse processo.

Dos frameworks que simulam uma interação de usuário no navegador, conheço o Cypress e Playwright. Também tem o Selenium, que eu ouvia falar mais antigamente, não sei se hoje em dia ainda é usado para testes.