Maximize a Performance dos Seus Testes com Jest e Vitest
Vinicius Rodrigues
Posted on October 2, 2024
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;
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' });
});
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;
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' });
})
})
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']
}
})
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.
Posted on October 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.