Testes em TypeScript: de unit a e2e — guia completo
\nO desenvolvimento web moderno é impensável sem testes de qualidade. TypeScript, como uma camada sobre JavaScript, fornece ferramentas poderosas para tipagem, mas não resolve o problema de erros lógicos. Neste artigo, vamos abordar três níveis de teste: unit (unitário), integration (integração) e e2e (ponta a ponta). Você aprenderá como configurar o ambiente, escrever testes eficazes em TypeScript e integrá-los no pipeline de CI/CD.
\n\n1. Testes unitários: a base da pirâmide de testes
\nTestes unitários verificam módulos isolados (funções, classes, métodos) sem dependências externas. Para TypeScript, a melhor escolha é o Jest — ele suporta TypeScript nativamente e fornece uma API rica para mocks e asserções.
\n\nConfiguração do Jest para TypeScript
\nnpm install --save-dev jest @types/jest ts-jest\nnpx ts-jest config:init\nO arquivo jest.config.ts deve conter:
export default {\n preset: 'ts-jest',\n testEnvironment: 'node',\n roots: ['<rootDir>/src'],\n testMatch: ['**/__tests__/**/*.test.ts']\n};\n\nExemplo de teste unitário de função
\nSuponha que temos uma função de cálculo de desconto:
\n// src/utils/discount.ts\nexport function calculateDiscount(price: number, percent: number): number {\n if (percent < 0 || percent > 100) throw new Error('Invalid percent');\n return price - (price * percent) / 100;\n}\nTeste para ela:
\n// src/__tests__/discount.test.ts\nimport { calculateDiscount } from '../utils/discount';\n\ndescribe('calculateDiscount', () => {\n it('deve retornar 80 para preço 100 e desconto de 20%', () => {\n expect(calculateDiscount(100, 20)).toBe(80);\n });\n\n it('deve lançar erro para porcentagem inválida', () => {\n expect(() => calculateDiscount(100, 150)).toThrow('Invalid percent');\n });\n\n it('deve retornar 0 com desconto de 100%', () => {\n expect(calculateDiscount(50, 100)).toBe(0);\n });\n});\n\nTrabalhando com mocks
\nPara isolar testes, use mocks. Exemplo com um serviço que depende do banco de dados:
\n// src/services/user.service.ts\nimport { db } from '../database';\n\nexport class UserService {\n async getUser(id: number) {\n const user = await db.findUser(id);\n if (!user) throw new Error('User not found');\n return user;\n }\n}\n// src/__tests__/user.service.test.ts\nimport { UserService } from '../services/user.service';\nimport { db } from '../database';\n\njest.mock('../database');\n\ndescribe('UserService', () => {\n it('deve retornar o usuário', async () => {\n const mockUser = { id: 1, name: 'Alice' };\n (db.findUser as jest.Mock).mockResolvedValue(mockUser);\n\n const service = new UserService();\n const result = await service.getUser(1);\n expect(result).toEqual(mockUser);\n });\n});\n\n2. Testes de integração: verificando a interação entre componentes
\nTestes de integração verificam como os módulos funcionam juntos: rotas de API, bancos de dados, serviços externos. Para aplicações TypeScript em Node.js, frequentemente usa-se Supertest em conjunto com Jest.
\n\nConfiguração do teste de integração para Express/NestJS
\nnpm install --save-dev supertest @types/supertest\nExemplo de teste para REST API (NestJS):
\n// src/__tests__/app.integration.test.ts\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication } from '@nestjs/common';\nimport * as request from 'supertest';\nimport { AppModule } from '../app.module';\n\ndescribe('AppController (e2e)', () => {\n let app: INestApplication;\n\n beforeAll(async () => {\n const moduleFixture: TestingModule = await Test.createTestingModule({\n imports: [AppModule],\n }).compile();\n\n a