Vitest em 2026: O Novo Padrão de Testes no JavaScript Moderno
Olá HaWkers, se você ainda está usando Jest em novos projetos, provavelmente está perdendo tempo e performance. Vitest se consolidou como o framework de testes padrão do ecossistema JavaScript moderno em 2026.
Vamos entender por que a mudança aconteceu e como você pode se beneficiar.
Por Que Vitest Venceu
O Contexto da Mudança
// Timeline da mudança de Jest para Vitest
const testingTimeline = {
2020: 'Jest é padrão indiscutível',
2022: 'Vitest 0.x surge com Vite',
2023: 'Vitest 1.0 - pronto para produção',
2024: {
vitest: 'Adoção massiva em novos projetos',
jest: 'Ainda dominante em legado'
},
2025: {
vitest4: 'Vitest 4.0 estável',
angular: 'Angular 21 adota Vitest como padrão',
breaking: 'Ponto de virada'
},
2026: {
status: 'Vitest é o novo padrão',
jest: 'Mantido para legado',
newProjects: '80%+ usam Vitest'
}
};Vantagens do Vitest
// Por que Vitest superou Jest
const vitestAdvantages = {
// 1. Performance
performance: {
speedup: '2-10x mais rápido que Jest',
why: 'Reutiliza pipeline do Vite',
watch: 'Watch mode instantâneo',
benchmark: `
Projeto médio (500 testes):
Jest: 45 segundos
Vitest: 8 segundos
`
},
// 2. Configuração zero com Vite
zeroConfig: {
if: 'Você usa Vite',
then: 'Vitest funciona sem config',
shared: 'Mesma config de plugins, aliases, etc',
dx: 'Não precisa manter duas configs'
},
// 3. ESM nativo
esm: {
jest: 'ESM é add-on, frequentemente quebra',
vitest: 'ESM é nativo, funciona perfeitamente',
impact: 'Pode testar código ESM-only sem hacks'
},
// 4. TypeScript nativo
typescript: {
jest: 'Requer ts-jest ou babel',
vitest: 'TypeScript funciona out of box',
types: 'Types incluídos, sem @types/vitest'
},
// 5. API compatível com Jest
compatibility: {
api: 'describe, it, expect - mesma API',
migration: 'Maioria dos testes funciona sem mudança',
mocks: 'vi.mock() similar a jest.mock()'
},
// 6. Features modernas
modernFeatures: {
browserMode: 'Testa no browser real (Playwright)',
snapshotInline: 'Snapshots inline no código',
coverage: 'v8 coverage nativo',
ui: 'UI interativa built-in'
}
};
Começando com Vitest
Setup Básico
// Instalação e configuração
// 1. Instalar
// npm install -D vitest
// 2. Adicionar script no package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}
// 3. Configuração (opcional - funciona sem config com Vite)
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// Globals (describe, it, expect) sem import
globals: true,
// Ambiente de teste
environment: 'jsdom', // ou 'node', 'happy-dom'
// Setup files
setupFiles: ['./tests/setup.ts'],
// Coverage
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
exclude: ['node_modules/', 'tests/']
},
// Incluir/excluir arquivos
include: ['**/*.{test,spec}.{ts,tsx}'],
},
});Escrevendo Testes
// Sintaxe básica - muito similar a Jest
// math.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { sum, multiply } from './math';
describe('Math functions', () => {
it('should sum two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
it('should multiply two numbers', () => {
expect(multiply(3, 4)).toBe(12);
});
});
// Com globals: true no config, pode omitir imports
describe('Math functions', () => {
it('should sum two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
});Mocking
// Mocking em Vitest
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { fetchUser } from './api';
import { UserService } from './UserService';
// Mock de módulo inteiro
vi.mock('./api', () => ({
fetchUser: vi.fn()
}));
describe('UserService', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should fetch user by id', async () => {
// Configurar mock
vi.mocked(fetchUser).mockResolvedValue({
id: 1,
name: 'John'
});
const service = new UserService();
const user = await service.getUser(1);
expect(fetchUser).toHaveBeenCalledWith(1);
expect(user.name).toBe('John');
});
});
// Mock de função específica
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue(asyncValue);
mockFn.mockImplementation((x) => x * 2);
// Spy em método existente
const spy = vi.spyOn(object, 'method');
spy.mockReturnValue('mocked');
// Mock de timers
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.runAllTimers();
vi.useRealTimers();
Testando Componentes
// Testando componentes React com Vitest + Testing Library
// Button.test.tsx
import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('should render with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
it('should call onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click</Button>);
await fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledOnce();
});
it('should be disabled when loading', () => {
render(<Button loading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});
// Setup para Testing Library (tests/setup.ts)
import '@testing-library/jest-dom/vitest';Testando Vue
// Testando componentes Vue com Vitest
// Counter.test.ts
import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import Counter from './Counter.vue';
describe('Counter', () => {
it('should render initial count', () => {
const wrapper = mount(Counter, {
props: { initialCount: 5 }
});
expect(wrapper.text()).toContain('5');
});
it('should increment when button clicked', async () => {
const wrapper = mount(Counter);
await wrapper.find('button').trigger('click');
expect(wrapper.text()).toContain('1');
});
});Features Avançadas
Browser Mode
// Testar no browser real com Playwright
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
enabled: true,
name: 'chromium', // ou 'firefox', 'webkit'
provider: 'playwright',
},
},
});
// Testes rodam no browser real!
// Útil para:
// - Canvas, WebGL
// - Web APIs específicas
// - Visual regression
// visual.test.ts
import { describe, it, expect } from 'vitest';
import { page } from '@vitest/browser/context';
describe('Visual tests', () => {
it('should render chart correctly', async () => {
await page.goto('/chart');
// Screenshot para visual regression
await expect(page.locator('.chart')).toMatchScreenshot();
});
});
Inline Snapshots
// Snapshots inline - mais conveniente que arquivos
import { describe, it, expect } from 'vitest';
import { formatUser } from './formatUser';
describe('formatUser', () => {
it('should format user object', () => {
const user = { name: 'John', age: 30 };
// Snapshot inline - atualiza no próprio arquivo!
expect(formatUser(user)).toMatchInlineSnapshot(`
{
"displayName": "John (30 years old)",
"initials": "J"
}
`);
});
});
// Quando você roda vitest -u, o snapshot é atualizado
// diretamente no arquivo de testeType Testing
// Testar tipos TypeScript
// types.test-d.ts (note o -d no nome)
import { describe, it, expectTypeOf } from 'vitest';
import { createStore } from './store';
describe('Store types', () => {
it('should infer state type correctly', () => {
const store = createStore({ count: 0 });
// Testa se o tipo está correto
expectTypeOf(store.state.count).toBeNumber();
expectTypeOf(store.state.count).not.toBeString();
});
it('should accept correct action types', () => {
type Action = { type: 'increment' } | { type: 'decrement' };
expectTypeOf<Action>().toMatchTypeOf<{ type: string }>();
});
});
// vitest.config.ts
export default defineConfig({
test: {
typecheck: {
enabled: true,
},
},
});UI Mode
// Interface visual para testes
// Rode: npx vitest --ui
// Features da UI:
// - Visualizar todos os testes
// - Filtrar por status (passed, failed, skipped)
// - Ver código do teste
// - Ver output de console
// - Re-run testes específicos
// - Module graph visualization
// Excelente para:
// - Debug de testes falhando
// - Entender cobertura
// - Onboarding de novos devs
Migrando de Jest para Vitest
Guia de Migração
// Passos para migrar
const migrationGuide = {
// 1. Instalar Vitest
step1: `
npm remove jest @types/jest ts-jest babel-jest
npm install -D vitest
`,
// 2. Criar config
step2: `
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true, // Para compatibilidade com Jest
environment: 'jsdom',
},
});
`,
// 3. Atualizar scripts
step3: `
// package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}
`,
// 4. Search and replace
step4: {
from: "jest.fn()",
to: "vi.fn()",
from2: "jest.mock(",
to2: "vi.mock(",
from3: "jest.spyOn(",
to3: "vi.spyOn(",
from4: "@jest/globals",
to4: "vitest"
},
// 5. Atualizar imports (se não usar globals)
step5: `
// Antes
import { jest } from '@jest/globals';
// Depois
import { vi } from 'vitest';
`
};Diferenças de API
// Diferenças entre Jest e Vitest
const apiDifferences = {
// Mocking
mocking: {
jest: 'jest.fn(), jest.mock(), jest.spyOn()',
vitest: 'vi.fn(), vi.mock(), vi.spyOn()',
note: 'Apenas prefixo diferente'
},
// Timers
timers: {
jest: 'jest.useFakeTimers()',
vitest: 'vi.useFakeTimers()',
extra: 'Vitest tem vi.advanceTimersByTimeAsync()'
},
// Module mocking
moduleMock: {
jest: `
jest.mock('./module', () => ({
foo: jest.fn()
}));
`,
vitest: `
vi.mock('./module', () => ({
foo: vi.fn()
}));
`,
vitestAlternative: `
// Vitest também suporta:
vi.mock('./module', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
foo: vi.fn()
};
});
`
},
// Snapshot
snapshot: {
jest: 'expect(x).toMatchSnapshot()',
vitest: 'expect(x).toMatchSnapshot()',
extra: 'Vitest tem toMatchInlineSnapshot()'
},
// Globals
globals: {
jest: 'Globals por padrão',
vitest: 'Precisa de globals: true ou imports explícitos',
recommendation: 'Use imports explícitos para type safety'
}
};Codemod Automático
// Use codemod para migração automatizada
// Instalar
// npx @vitest/codemod jest
// O codemod faz:
// - Renomeia jest.* para vi.*
// - Atualiza imports
// - Ajusta configuração
// Depois, revise manualmente:
// - Mocks complexos
// - Setup files
// - Custom matchers
Boas Práticas com Vitest
Organização de Testes
// Estrutura recomendada
const testStructure = {
// Opção 1: Co-located (recomendado)
colocated: `
src/
components/
Button/
Button.tsx
Button.test.tsx ← Junto do código
Button.stories.tsx
utils/
format.ts
format.test.ts ← Junto do código
`,
// Opção 2: Pasta separada
separate: `
src/
components/
Button.tsx
tests/
components/
Button.test.tsx ← Pasta separada
`,
recommendation: `
Co-located é melhor:
- Fácil encontrar testes
- Refatorar junto
- Incentiva escrever testes
`
};Patterns Úteis
// Patterns que funcionam bem
// 1. Factory functions para fixtures
function createUser(overrides = {}) {
return {
id: 1,
name: 'John',
email: 'john@example.com',
...overrides
};
}
it('should update user name', () => {
const user = createUser({ name: 'Jane' });
// ...
});
// 2. Custom matchers
expect.extend({
toBeValidEmail(received) {
const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(received);
return {
pass,
message: () => `expected ${received} to be valid email`
};
}
});
// 3. Testing utilities
function renderWithProviders(ui, options = {}) {
return render(ui, {
wrapper: ({ children }) => (
<QueryClientProvider client={queryClient}>
<ThemeProvider>
{children}
</ThemeProvider>
</QueryClientProvider>
),
...options
});
}
// 4. Describe blocks organizados
describe('UserService', () => {
describe('getUser', () => {
it('should return user when found', () => {});
it('should throw when not found', () => {});
});
describe('createUser', () => {
it('should create user with valid data', () => {});
it('should reject invalid email', () => {});
});
});
Conclusão
Vitest se tornou o padrão de testes em JavaScript por boas razões: é mais rápido, mais moderno, e tem melhor DX que Jest. A migração é simples e os benefícios são imediatos.
Quando usar Vitest:
- Novos projetos (sempre)
- Projetos com Vite (migração fácil)
- Projetos que precisam de ESM
- Quando performance de testes importa
Quando manter Jest:
- Projetos legados funcionando bem
- Custo de migração não compensa
- Dependências que requerem Jest
Ações recomendadas:
- Use Vitest em novos projetos
- Planeje migração de projetos ativos
- Explore browser mode para testes visuais
- Use UI mode para debug
Para entender mais sobre o ecossistema JavaScript moderno, leia: VoidZero 2026.

