Maximize a Performance dos Seus Testes com Jest e Vitest

vinicius_rodrigues

Vinicius Rodrigues

Posted on October 2, 2024

Maximize a Performance dos Seus Testes com Jest e Vitest

Introdução

Se você já criou testes automatizados para seu projeto, com certeza conhece o jest. Não é por acaso: O jest é uma das ferramentas de teste mais utilizadas no mercado e amplamente difundida pela comunidade de desenvolvimento.

Mas, independentemente da tecnologia, um código mal otimizado vai rodar mal. E se você estiver em um projeto grande, o simples ato de executar testes pode demorar o tempo de tomar um café (ou vários).

Eu não tenho nada contra sua pausa pro cafezinho, estou tomando um enquanto escrevo este artigo, mas ultimamente eu entrei de cabeça em uma missão: otimizar testes para que sejam o mais rápidos possível.

Bora pro código!

O cenário caótico

Para te mostrar isso eu vou propor um cenário caótico, uma API escrita em typescript e usando Fastify como web server. Os testes contém um problema de otimização e o Jest não tem nenhuma configuração além do básico para executar os testes.

//slow jest config

import { type Config } from '@jest/types';

const config: Config.InitialOptions = {
    preset: 'ts-jest',
    transform: {
        '^.+\\.tsx?$': 'ts-jest'
    },
    testMatch: [
        '**/slow.*'
    ]
};

export default config;
Enter fullscreen mode Exit fullscreen mode

Você pode notar que o Jest usa uma outra biblioteca chamada ts-jest para conseguir ler nossos testes utilizando typescript. Esse já é nosso primeiro ponto de melhoria. Além disso podemos definir melhor nosso target, ou seja, onde o Jest vai buscar nossos testes.

Quanto aos testes, criei um arquivo com 300 testes iguais:

it('should pass', async () => {
    const app = await buildServer()

    const response = await app.inject({
      method: 'GET',
      url: '/check',
    })

    expect(response.statusCode).toEqual(200)
    expect(response.json()).toEqual({ hello: 'world' });
 });
Enter fullscreen mode Exit fullscreen mode

Nosso erro premeditado aqui é que cada teste recria a instância do Fastify para cada execução, portanto, 300 vezes. Você pode pensar que isso é muito específico mas na verdade é uma forma de mostrar problemas que acontecem de várias formas e causam lentidão, por exemplo:

  • Instancias sendo recriadas (como no exemplo)
  • Alterações no banco de dados
  • Mocks
  • Validações desnecessárias

Otimizando o Jest

Vamos adicionar algumas configurações.

  • maxWorkers: Define quantos núcleos da CPU serão usados para os testes em paralelo, aqui configurado para usar 50%.
  • testEnvironment: Define o ambiente de execução, que neste caso é o "node".
  • testPathIgnorePatterns: Ignora diretórios como node_modules e src.

Nossa grande estrela aqui é o @swc/jest. Diferente do ts-jest, que transpila TypeScript usando o compilador do próprio TypeScript, o @swc/jest utiliza o SWC, um compilador escrito em Rust que é muito mais rápido para transformar arquivos TypeScript em JavaScript. O Rust, sendo uma linguagem de baixo nível, oferece desempenho superior, especialmente em grandes bases de código.

Além disso, configuramos também um caminho bem específico para o jest encontrar nosso teste em testMatch. Sei que não é um caso real mas você também poderia mudar para algo como **/*.test.ts e isso faria com que arquivos com outras extensões fossem ignorados.

// Fast jest config

import {
    type Config
} from '@jest/types';

const config: Config.InitialOptions = {
    cache: true,
    maxWorkers: '50%',
    testEnvironment: "node",
    testPathIgnorePatterns: [
        'node_modules',
        'src'
    ],
    transform: {
        '^.+\\.tsx?$': '@swc/jest'
    },
    testMatch: [
        '**/slow.test.ts'
    ]
};

export default config;
Enter fullscreen mode Exit fullscreen mode

Feito isso, vamos fazer a comparação de ambas as configurações de Jest executando nossos testes que ainda tem o problema de otimização.

Tentativa Slow Jest Fast Jest
1 3.728s 2.939s
2 3.701s 2.982s
3 3.687s 2.904s
4 3.763s 2.934s
5 3.730s 2.911s

Médias:

  • Slow Jest: 3.722s
  • Fast Jest: 2.934s

Tivemos uma melhora de 21,17% com nosso Jest otimizado. Agora vamos corrigir o problema no nosso teste.

Otimizando o teste

Como disse antes, nosso caso não é tão complexo. O que vamos fazer aqui é mover a criação da instancia para nosso beforeAll e reutilizar em todos os testes.
O importante aqui é frisar que diferentes projetos podem gerar situações parecidas com essa e depende de você ter o senso crítico para entender e corrigir essas falhas.

describe('fast', () => {
    let app: FastifyInstance;
    beforeAll(async () => {
        app = await buildServer()
    })

    it('should pass', async () => {
        const response = await app.inject({
            method: 'GET',
            url: '/check',
        })

        expect(response.statusCode).toEqual(200)
        expect(response.json()).toEqual({ hello: 'world' });
    })
})
Enter fullscreen mode Exit fullscreen mode

Com o problema corrigido vamos a uma nova execução.

Tentativa Slow Jest Fast Jest
1 3.318s 2.593s
2 3.316s 2.552s
3 3.302s 2.538s
4 3.328s 2.567s
5 3.278s 2.566s

Médias:

  • Slow Jest: 3.3044s
  • Fast Jest: 2.5632s

Agora temos uma nova melhoria nos tempos. Comparando a média do nosso pior cenário (3.722s) com o melhor cenário atual (2.5632s) temos uma melhoria de 31.1%.

Podemos melhorar?

Se você não tem a opção de mudar o Jest por outra ferramenta acredito que essa melhoria seja algo ótimo para você. Mas podemos ir além utilizando o Vitest.

Expandindo os horizontes com Vitest

Se você nunca ouviu falar do Vitest vou resumir, é uma biblioteca de testes compatível com o Jest e que usa o EsBuild por baixo dos panos. Sendo assim, você quase não precisa refatorar os testes escritos pro jest e o arquivo de configuração é muito simples e no nosso caso é opcional. Mas vou deixar as configurações que adicionei no nosso projeto:

import { defineConfig } from 'vitest/config'

export default defineConfig({
    test: {
        globals: true,
        environment: 'node',
        include: ['tests/fast.test.ts']
    }
})
Enter fullscreen mode Exit fullscreen mode

Indo direto ao ponto, vou executar apenas o teste que foi corrigido e vamos comparar os resultados.

Tentativa Slow Jest Fast Jest Vitest
1 3.287s 2.575s 1.690s
2 3.295s 2.564s 1.770s
3 3.279s 2.589s 1.711s
4 3.311s 2.555s 1.708s
5 3.291s 2.567s 1.757s

Médias:

  • Slow Jest: 3.293s
  • Fast Jest: 2.570s
  • Vitest: 1.727s

Comparando com o melhor tempo do Jest, o Vitest é 32% mais rápido. Quando olhamos para o nosso primeiro caso de testes o ganho é de 50%

Espero que tenha gostado! Fique a vontade para deixar sugestões e compartilhar com seus amigos e colegas de trabalho.

💖 💪 🙅 🚩
vinicius_rodrigues
Vinicius Rodrigues

Posted on October 2, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related