Pular para o conteúdo principal

TDD com Cursor: Unitário, Integração e E2E

Cursor Testing Guide

Testes são essenciais para manter a qualidade do código e a confiança nos deployments. Os recursos de IA do Cursor ajudam a gerar testes, sugerir casos de borda e alcançar melhor cobertura, tornando o Desenvolvimento Orientado a Testes (TDD) mais acessível. Este guia aborda fluxos de trabalho de testes unitários, de integração e end-to-end.

Pré-requisitos​

Antes de começar, você precisará de:

  • Cursor instalado (versão mais recente)
  • Node.js instalado
  • Noções básicas de conceitos de testes

Testes Unitários com Jest​

Configuração do Jest​

Instale o Jest e pacotes relacionados:

npm install --save-dev jest @types/jest ts-jest

Configure jest.config.js:

module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};

Gerando Testes Unitários com Cursor​

Selecione uma função e peça ao Cursor para gerar testes:

// src/utils/calculator.ts
export const calculateDiscount = (
price: number,
discountPercent: number,
maxDiscount?: number
): number => {
if (price < 0 || discountPercent < 0 || discountPercent > 100) {
throw new Error('Invalid input');
}

const discount = (price * discountPercent) / 100;
const finalDiscount = maxDiscount ? Math.min(discount, maxDiscount) : discount;

return Number((price - finalDiscount).toFixed(2));
};

Prompt:

Escreva testes unitários Jest completos para esta função. Devem incluir:
- Testes de caminho feliz
- Casos de borda (0, negativos, valores limite)
- Casos de erro
- Testes para o parâmetro maxDiscount

Resultado gerado pelo Cursor:

// src/utils/__tests__/calculator.test.ts
import { calculateDiscount } from '../calculator';

describe('calculateDiscount', () => {
describe('happy path', () => {
it('should calculate 10% discount correctly', () => {
expect(calculateDiscount(100, 10)).toBe(90);
});

it('should calculate 50% discount correctly', () => {
expect(calculateDiscount(200, 50)).toBe(100);
});
});

describe('maxDiscount parameter', () => {
it('should apply maxDiscount when discount exceeds it', () => {
expect(calculateDiscount(1000, 50, 100)).toBe(900);
});

it('should ignore maxDiscount when discount is smaller', () => {
expect(calculateDiscount(100, 10, 50)).toBe(90);
});
});

describe('edge cases', () => {
it('should return original price with 0% discount', () => {
expect(calculateDiscount(100, 0)).toBe(100);
});

it('should return 0 with 100% discount', () => {
expect(calculateDiscount(100, 100)).toBe(0);
});

it('should handle decimal prices', () => {
expect(calculateDiscount(99.99, 10)).toBe(89.99);
});
});

describe('error cases', () => {
it('should throw error for negative price', () => {
expect(() => calculateDiscount(-10, 10)).toThrow('Invalid input');
});

it('should throw error for negative discount', () => {
expect(() => calculateDiscount(100, -5)).toThrow('Invalid input');
});

it('should throw error for discount over 100%', () => {
expect(() => calculateDiscount(100, 101)).toThrow('Invalid input');
});
});
});

Testes de Componentes React​

// src/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';

describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});

it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});

it('is disabled when loading', () => {
render(<Button isLoading>Loading</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});

Vitest para Projetos Modernos​

Configuração do Vitest​

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
},
});

Exemplo de Teste Vitest​

// src/utils/formatDate.test.ts
import { describe, it, expect } from 'vitest';
import { formatDate } from './formatDate';

describe('formatDate', () => {
it('formats ISO date to readable string', () => {
const result = formatDate('2024-01-15');
expect(result).toBe('January 15, 2024');
});

it('handles invalid dates gracefully', () => {
expect(() => formatDate('invalid')).toThrow();
});
});
Vitest vs Jest

O Vitest oferece suporte nativo a TypeScript, execução mais rápida e melhor compatibilidade com ESM. Use Vitest para novos projetos e mantenha o Jest se você tem uma suíte de testes existente.

Testes de Integração​

Testes de Integração de API​

// tests/integration/users.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createApp } from '../../src/app';
import { setupTestDB, teardownTestDB } from '../helpers/database';

const app = createApp();

describe('Users API Integration', () => {
beforeAll(async () => {
await setupTestDB();
});

afterAll(async () => {
await teardownTestDB();
});

describe('POST /api/users', () => {
it('creates a user and persists to database', async () => {
const response = await app.request('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Integration Test',
password: 'password123',
}),
});

expect(response.status).toBe(201);
const body = await response.json();
expect(body.data.email).toBe('[email protected]');
expect(body.data.id).toBeDefined();
});
});
});

Configuração de Integração com Banco de Dados​

// tests/helpers/database.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export async function setupTestDB() {
// Run migrations
// Seed test data
}

export async function teardownTestDB() {
// Clean up test data
await prisma.$executeRaw`TRUNCATE TABLE users CASCADE`;
await prisma.$disconnect();
}

Testes End-to-End​

Configuração do Cypress​

npm install --save-dev cypress
npx cypress open

Escrevendo Testes E2E com Cursor​

// cypress/e2e/auth.cy.js
describe('Authentication Flow', () => {
beforeEach(() => {
cy.visit('/login');
});

it('allows user to log in with valid credentials', () => {
cy.get('[data-testid="email-input"]').type('[email protected]');
cy.get('[data-testid="password-input"]').type('password123');
cy.get('[data-testid="login-button"]').click();

cy.url().should('include', '/dashboard');
cy.get('[data-testid="welcome-message"]').should('contain', 'Welcome');
});

it('shows error for invalid credentials', () => {
cy.get('[data-testid="email-input"]').type('[email protected]');
cy.get('[data-testid="password-input"]').type('wrongpassword');
cy.get('[data-testid="login-button"]').click();

cy.get('[data-testid="error-message"]')
.should('be.visible')
.and('contain', 'Invalid credentials');
});
});

Alternativa com Playwright​

// tests/e2e/checkout.spec.ts
import { test, expect } from '@playwright/test';

test('user can complete checkout', async ({ page }) => {
await page.goto('/products');

// Add item to cart
await page.click('[data-testid="add-to-cart-1"]');
await page.click('[data-testid="cart-icon"]');

// Proceed to checkout
await page.click('[data-testid="checkout-button"]');

// Fill shipping info
await page.fill('[name="address"]', '123 Test St');
await page.fill('[name="city"]', 'Test City');
await page.selectOption('[name="country"]', 'US');

// Complete order
await page.click('[data-testid="place-order"]');

await expect(page.locator('[data-testid="order-confirmation"]')).toBeVisible();
});

Cobertura e Qualidade de Testes​

Relatórios de Cobertura​

Gere e visualize cobertura:

# Jest
npm test -- --coverage

# Vitest
npx vitest --coverage

Melhoria de Cobertura com IA​

Compartilhe seu relatório de cobertura com o Cursor:

Minha cobertura de testes está em 65%. As linhas não cobertas são:
[Colar relatório de cobertura]

Sugira testes para cobrir esses casos de borda.

Melhores Práticas de Testes​

PráticaDescrição
Padrão AAAArrange, Act, Assert - estruture todos os testes claramente
Uma asserção por testeMantenha testes focados e legíveis
Nomes descritivosNomes de testes devem ler como documentação
Mock de dependências externasIsole unidades mockando banco de dados e APIs
Teste comportamento, não implementaçãoTestes seguros para refatoração verificam resultados
Use data-testidAdicione seletores estáveis para testes E2E

Utilitários e Helpers de Teste​

// src/test/factories.ts
import { faker } from '@faker-js/faker';

export const createUserFixture = (overrides = {}) => ({
id: faker.string.uuid(),
email: faker.internet.email(),
name: faker.person.fullName(),
createdAt: new Date(),
...overrides,
});

export const createProductFixture = (overrides = {}) => ({
id: faker.string.uuid(),
name: faker.commerce.productName(),
price: Number(faker.commerce.price()),
...overrides,
});

Executando Testes no Cursor​

Integração com Terminal​

# Run all tests
npm test

# Run with watch mode
npm test -- --watch

# Run specific file
npm test -- src/utils/calculator.test.ts

# Run with coverage
npm test -- --coverage

Explorador de Testes​

O Cursor suporta o explorador de testes do VS Code:

  1. Abra a barra lateral de testes
  2. Veja todos os testes organizados por arquivo
  3. Clique para executar testes individuais
  4. Veja status de pass/fail inline

Integração Contínua​

Exemplo com GitHub Actions​

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
- run: npm run test:e2e

Referência Rápida​

TarefaPrompt do Cursor
Gerar testes unitários"Write Jest tests for this function with edge cases"
Mock de API externa"Create a mock for this API client using MSW"
Corrigir teste quebrado"This test is failing with [error]. Fix it."
Melhorar cobertura"Suggest tests to improve coverage for this file"
Teste E2E de fluxo"Write a Cypress test for the user checkout flow"

Recursos Relacionados​

Última atualização: Julho de 2025