Vue.js: Testes Unitários e E2E
Última atualização: 2026-08-26
Os testes unitários são a base para garantir a qualidade do código — eles permitem refatorar componentes sem medo de causar falhas, possibilitam que a integração contínua (CI) execute testes automaticamente e dão aos novos membros da equipe a confiança necessária para fazer alterações no código. O Vue 3 recomenda o Vitest (um framework de testes de última geração que é 10 vezes mais rápido que o Jest) + o Vue Test Utils (a ferramenta oficial para testes de componentes).
Os testes E2E simulam interações reais dos usuários — o Playwright é, atualmente, a ferramenta de testes E2E mais popular (mais rápida que o Cypress e compatível com vários navegadores). Este curso aborda uma estrutura abrangente de testes que inclui tanto testes unitários quanto testes E2E.
1. O que você vai aprender
- Configuração do Vitest e as 4 APIs principais
- Montagem de componentes com o Vue Test Utils (@vue/test-utils)
- 5 principais cenários de teste de componentes (props / emit / slot / event / async)
- 5 técnicas fundamentais de afirmação
- Testes de ponta a ponta do Playwright
- Relatório de cobertura
- 5 erros comuns
2. Os pontos críticos de uma equipe que sofre de “fobia de refatoração”
(1) Problema: Se um componente for modificado incorretamente, todo o site trava
A equipe da Alice contava com um painel de administração de comércio eletrônico com mais de 100 componentes:
// ❌ The "Broken" Version:Not tested,Alice I don't dare to refactor
// ProductCard.vue Changed 1 line, The entire home page is a blank screen
// No one noticed, Day 2 not until a user filed a complaint they discovered
// Team:"Don't move this component,We don't know what it will destroy."
O gerente de produto Charlie:
“Alice, precisamos de testes. Queremos poder refatorar sem medo. Precisamos de 1) testes de unidade para os componentes, 2) testes de ponta a ponta para os fluxos críticos.”
(2) Solução de utilitários de teste Vitest + Vue
// tests/unit/ProductCard.test.js
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import ProductCard from '@/components/ProductCard.vue'
describe('ProductCard', () => {
it('renders product name and price', () => {
const product = { id: 1, name: 'iPhone', price: 999 }
const wrapper = mount(ProductCard, { props: { product } })
expect(wrapper.text()).toContain('iPhone')
expect(wrapper.text()).toContain('$999')
})
it('emits add-to-cart event', async () => {
const wrapper = mount(ProductCard, {
props: { product: { id: 1, name: 'iPhone', price: 999 } }
})
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('add-to-cart')).toBeTruthy()
expect(wrapper.emitted('add-to-cart')[0]).toEqual([1])
})
})
Execute o teste:
$ npx vitest run
✓ ProductCard > renders product name and price
✓ ProductCard > emits add-to-cart event
2 passed
Executa em 5 segundos, sem alarmes falsos. Você saberá imediatamente após fazer uma alteração no código se ele está com defeito.
(3) Receita
Após adicionar os testes:
- Recuperar a confiança: melhora de 100% (sem medo de piorar a situação)
- Detecção precoce de bugs: 80% são identificados durante a fase de desenvolvimento
- Objetivo da documentação: O teste serve como guia do usuário para o componente.
- Automação de CI: Executar testes automaticamente em pull requests
3. Configuração do Vitest
(1) Instalação
npm install -D vitest @vue/test-utils @vitest/coverage-v8 jsdom
(2) Configuração básica
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
test: {
// Test Environment
environment: 'jsdom', // or 'happy-dom' / 'node'
// Matching Files
include: ['tests/**/*.test.ts', 'src/**/*.test.ts'],
// Coverage Rate
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'json'],
exclude: ['node_modules/', 'tests/']
},
// Global setup
setupFiles: ['./tests/setup.ts']
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})
(3) 4 APIs principais
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
// 1. describe:Test Suite
describe('Calculator', () => { })
// 2. it:Single Test
it('adds 1 + 1 to equal 2', () => { })
// 3. expect:Assertion
expect(1 + 1).toBe(2)
expect(arr).toContain('apple')
expect(obj).toMatchObject({ name: 'Alice' })
// 4. vi:mock / spy
vi.fn() // mock Function
vi.mock('./api') // mock Module
vi.spyOn(obj, 'method') // spy Methods
(4) Vitest x Jest
| Dimensão | É | Vitest |
|---|---|---|
| Tempo de inicialização | 3–5 s | < 1 s |
| Modo de escuta | 2–3 s | < 100 ms |
| Suporte ao ESM | Requer configuração | Nativo |
| TypeScript | Requer o ts-jest | Nativo |
| Compatibilidade | Abrangente | API compatível com o Jest |
| Avaliação | Antigo | ⭐⭐⭐⭐⭐ |
4. Testando os componentes do Vue Test Utils
(1) 5 APIs principais
import { mount, shallowMount, RouterLinkStub } from '@vue/test-utils'
// 1. mount:Full Mount(Child components are also mounted)
const wrapper = mount(Component, { props: { ... } })
// 2. shallowMount: Shallow Mount (Child components are stubbed)
const wrapper = shallowMount(Component, { props: { ... } })
// 3. Find an Element
wrapper.find('button') // First match
wrapper.findAll('li') // All matches
wrapper.findComponent(MyComponent) // Find Child Components
wrapper.get('#submit') // Must be found(Error not found)
// 4. Trigger Event
await wrapper.find('button').trigger('click')
await wrapper.find('input').setValue('hello')
// 5. Assertion
expect(wrapper.text()).toContain('Hello')
expect(wrapper.find('h1').text()).toBe('Title')
expect(wrapper.emitted('add-to-cart')).toBeTruthy()
(2) 5 principais cenários de teste
// 1. props Test
it('renders props correctly', () => {
const wrapper = mount(MyComponent, { props: { title: 'Hello' } })
expect(wrapper.text()).toContain('Hello')
})
// 2. emit Test
it('emits event on click', async () => {
const wrapper = mount(MyComponent)
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('submit')).toBeTruthy()
})
// 3. slot Test
it('renders default slot', () => {
const wrapper = mount(MyComponent, {
slots: { default: '<p>Slot content</p>' }
})
expect(wrapper.html()).toContain('Slot content')
})
// 4. Asynchronous Testing
it('loads data on mount', async () => {
const wrapper = mount(MyComponent)
await flushPromises() // Wait for asynchronous completion
expect(wrapper.text()).toContain('Loaded')
})
// 5. provide/inject Test
it('uses provided value', () => {
const wrapper = mount(MyComponent, {
provide: { theme: 'dark' }
})
expect(wrapper.find('.dark').exists()).toBe(true)
})
(3) 5 técnicas fundamentais de afirmação
// 1. The text contains
expect(wrapper.text()).toContain('Hello')
// 2. class Existence
expect(wrapper.find('.active').exists()).toBe(true)
// 3. Elemental Presence
expect(wrapper.find('button').exists()).toBe(true)
// 4. Event Trigger
expect(wrapper.emitted('click')).toBeTruthy()
expect(wrapper.emitted('click')[0]).toEqual([arg1, arg2])
// 5. Asynchronous operation completed
await wrapper.vm.$nextTick()
await flushPromises()
5. Testes de ponta a ponta do Playwright
(1) Instalação
npm install -D @playwright/test
npx playwright install
(2) Configuração
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:5173',
trace: 'on-first-retry'
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } }
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI
}
})
(3) 5 principais cenários de testes de ponta a ponta
// 1. Login Process
import { test, expect } from '@playwright/test'
test('user can login', async ({ page }) => {
await page.goto('/login')
await page.fill('input[name="username"]', 'alice@example.com')
await page.fill('input[name="password"]', 'password123')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
await expect(page.locator('h1')).toContainText('Welcome, Alice')
})
// 2. Add to Cart
test('add product to cart', async ({ page }) => {
await page.goto('/products/1')
await page.click('button:has-text("Add to Cart")')
await expect(page.locator('.cart-count')).toHaveText('1')
})
// 3. Form Submission
test('submit form with validation', async ({ page }) => {
await page.goto('/contact')
await page.fill('input[name="email"]', 'invalid-email')
await page.click('button[type="submit"]')
await expect(page.locator('.error')).toBeVisible()
})
// 4. Route Redirection
test('navigate to products page', async ({ page }) => {
await page.goto('/')
await page.click('a:has-text("Products")')
await expect(page).toHaveURL('/products')
})
// 5. Asynchronous Loading
test('loads data after navigation', async ({ page }) => {
await page.goto('/dashboard')
// Waiting for data to load
await page.waitForSelector('.chart-loaded')
await expect(page.locator('.chart-loaded')).toBeVisible()
})
(4) Executar testes de ponta a ponta
# Run all tests
npx playwright test
# Run a Specific File
npx playwright test tests/e2e/login.spec.ts
# Debug Mode(Open your browser)
npx playwright test --debug
# UI Pattern(Visualization)
npx playwright test --ui
6. Exemplo completo: 5 cenários principais de teste
▶ Exemplo: 1. Testando os 5 principais componentes
// 1. props
it('renders props', () => { ... })
// 2. emit
it('emits event', async () => { ... })
// 3. slot
it('renders slot', () => { ... })
// 4. async
it('loads data', async () => { ... })
// 5. provide
it('uses provide', () => { ... })
▶ Exemplo: 2. 5 técnicas-chave de afirmação
// 1. Text
expect(wrapper.text()).toContain('Hello')
// 2. class
expect(wrapper.find('.active').exists()).toBe(true)
// 3. Element
expect(wrapper.find('button').exists()).toBe(true)
// 4. Event
expect(wrapper.emitted('click')).toBeTruthy()
// 5. Asynchronous
await wrapper.vm.$nextTick()
▶ Exemplo: 3. 5 principais cenários de ponta a ponta (E2E)
// 1. Log In
test('login', async ({ page }) => { ... })
// 2. Shopping Cart
test('add to cart', async ({ page }) => { ... })
// 3. Form
test('form submit', async ({ page }) => { ... })
// 4. Routing
test('navigate', async ({ page }) => { ... })
// 5. Asynchronous
test('async load', async ({ page }) => { ... })
▶ Exemplo: 4. Referência rápida para 5 erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
| O teste não consegue localizar o componente | Erro de importação | Verifique os aliases de caminho |
| Testes assíncronos sem espera | Falha sempre | Use await + flushPromises |
| Evento de acionamento não está funcionando | O ouvinte não foi acionado | "await" após "trigger" |
| Tempo limite de E2E | Elemento não encontrado | waitForSelector |
| Baixa cobertura | Sem provas escritas | Limite de cobertura obrigatório |
▶ Exemplo: 5. Comparação das 5 principais ferramentas de teste
| Ferramenta | Velocidade | Aplicação |
|---|---|---|
| Vitest | ⭐⭐⭐⭐⭐ | Testes unitários |
| Utilitários de teste para Vue | ⭐⭐⭐⭐⭐ | Testes de componentes |
| Dramaturgo | ⭐⭐⭐⭐ | Testes de ponta a ponta |
| Cypress | ⭐⭐⭐ | E2E (Lento) |
| Testing Library | ⭐⭐⭐⭐ | Testes de componentes |
❓ Perguntas Frequentes
P: O que devo escolher, Vitest ou Jest? R: Vitest. Use o Vitest para todos os novos projetos (10 vezes mais rápido, suporte nativo a ESM/TS). Use o Jest para a manutenção de projetos legados.
P: O que devo escolher,
mountoushallowMount? R: UseshallowMountpara testes unitários (mais rápidos e mais isolados). Usemountpara testes de integração (mais realistas). Recomendação: useshallowMountpara testes de componentes emountpara alguns componentes-chave.
P: Qual é a taxa de cobertura de teste adequada? R: 80% ou mais para componentes principais, 90% ou mais para utilitários e 60% ou mais no total. Não há necessidade de buscar 100% (isso é impraticável).
P: Playwright x Cypress — qual devo escolher? R: Playwright. É quase duas vezes mais rápido, é compatível com vários navegadores (Chromium, Firefox, WebKit) e possui uma API melhor. O Cypress é compatível apenas com navegadores baseados no Chromium.
P: Qual é a proporção entre testes unitários e testes E2E? R: 70% de testes unitários + 20% de testes de integração + 10% de testes E2E. Os testes E2E são lentos e frágeis, enquanto os testes unitários oferecem a melhor relação custo-benefício.
P: Todas as dependências devem ser simuladas durante os testes? R: Para testes de unidade, simule APIs externas (fetch, localStorage). Para testes de integração, use dependências reais. Para testes de ponta a ponta (E2E), use tudo como está.
P: Como faço para executar testes com CI? R: Adicione a etapa
npm testno GitHub Actions ou no GitLab CI. Bloqueie a fusão de PRs caso os testes falhem.
📖 Resumo
- O Vitest é a estrutura de testes recomendada para o Vue 3 (10 vezes mais rápida que o Jest)
- Utilitários de teste do Vue: mount / shallowMount / 5 APIs principais
- 5 tipos de testes de componentes: props / emit / slot / async / provide
- 5 tipos de asserção: texto / classe / elemento / evento / assíncrono
- O Playwright é a ferramenta E2E recomendada (rápida / compatível com vários navegadores)
- Cobertura de testes: 80% ou mais no núcleo, 60% ou mais no total
- 70% de testes unitários + 20% de testes de integração + 10% de testes de ponta a ponta
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Escreva três testes para o ProductCard:
- renderização de props
- emitir um evento
- Caso limite (o estoque é 0)
-
Problemas avançados (Dificuldade: ⭐⭐)
Escreva testes abrangentes para o carrinho de compras
store:- 5 ações (addItem / removeItem / clear / load / save)
- 3 métodos getter (itemCount / totalPrice / isEmpty)
- Configuração do teste Pinia
-
Problema de desafio (Dificuldade: ⭐⭐⭐)
Implementar uma “estrutura de testes” abrangente:
- 10 testes unitários para componentes (3 a 5 testes por componente)
- 5 testes combináveis
- 5 testes de ponta a ponta (login / cadastro / pesquisa / carrinho de compras / finalização da compra)
- Limite de cobertura: 80%
- Integração de CI (GitHub Actions)
- Testes em vários navegadores com o Vitest e o Playwright