Módulo 2 · Ciclo Comum · IN02 · Aula 5 de 11
Back-End I — TDD top-down
Do controller à camada física — escrevemos os testes primeiro, do topo para a base, e implementamos o mínimo para passar.
🔷 TypeScript
🚂 Express
🧪 Jest+supertest
🔴🟢♻️ TDD
🏗️ MVC 6 camadas
⏱️ Daily — 15 Minutos
O que você fez? O que vai fazer? Algum impedimento?
15:00
✅ O que fiz 🎯 O que vou fazer 🚧 Impedimentos 📦 Progresso do projeto
📋 Agenda da Aula 5
Estrutura e objetivos da aula de hoje

🕐 Bloco 1 — Autoestudo (revisão e dúvidas)

Recap dos principais pontos do material lido — TS, Jest, supertest, TDD top-down.

🕑 Bloco 2 — Instrução (Professor)

Setup TS + Jest, RED → GREEN → REFACTOR aplicado a controller, service, repository e helpers.

🕒 Bloco 3 — Almoço

Pausa para descansar a cabeça e recarregar para o desenvolvimento da tarde.

🎯 Bloco 4 — Desenvolvimento (Projeto)

Aplicar o ciclo TDD na entidade do projeto até obter a casca verde do back-end.

🗺️ Roadmap do Módulo 2 — onde estamos
Saímos do banco e entramos no back-end. Hoje a casca; nas próximas, o miolo.
1
Dev Web — Frontend Básico
HTML, CSS, primeiras telas
2
Banco de Dados I
Relacional, normalização, DDL
3
Banco de Dados II
CRUD, transações
4
Banco de Dados III
JOINs, agregações, EXPLAIN
5
Back-End I — TDD top-down ◀ você está aqui
TS + Jest + supertest, controller → service → repository → helpers
6
Back-End II
CRUD HTTP completo, validações, erros
7
Front + Back I (SSR EJS)
Renderização no servidor
8–11
Integrações, deploy, encerramento
CI, métricas, qualidade
Por que sair do SQL puro? Por que TDD top-down?
Da query para o servidor — com testes guiando cada linha de código

🔷 Por que TypeScript no back-end?

  • Tipagem estática — erros pegos antes de rodar.
  • Modelos como contratosinterface User é a verdade compartilhada entre camadas.
  • Refactor seguro — renomear um campo quebra a compilação onde estiver errado.
  • DX — autocomplete preciso, sem adivinhação.

🔴 Por que TDD top-down?

  • Começamos pelo uso real (HTTP) e descemos até a base.
  • Cada teste falhando é uma especificação executável.
  • Implementamos o mínimo para passar — sem invenção.
  • Feedback de segundos com o ciclo RED → GREEN → REFACTOR.

🎯 A meta de hoje

Sair do dia com um servidor Express em TypeScript respondendo GET /users/:id, com testes verdes em todas as camadas — controller, service, repository, helper.

⚙️ Setup do projeto TypeScript
npm init, deps, tsconfig, scripts — tudo declarativo
terminal — bootstrap

📦 Dependências

Runtime: express, pg, dotenv.

Dev: typescript, tsx, @types/*, jest, ts-jest, supertest.

🧾 Scripts npm

  • devtsx watch src/server.ts
  • buildtsc
  • startnode dist/server.js
  • testjest
  • test:watchjest --watch
🧪 Setup Jest + ts-jest + supertest
Roda .ts direto, sem build prévio
jest.config.ts

✅ O que esse setup garante

  • preset: 'ts-jest' — compila TS em memória.
  • testEnvironment: 'node' — sem JSDOM.
  • testMatch aponta para *.spec.ts ao lado de cada arquivo.
  • supertest dispara HTTP no app sem subir porta.

🤔 AAA — Arrange / Act / Assert

Todo teste tem três blocos: preparar entrada, executar a ação, verificar o resultado. Esse padrão deixa o teste lido como uma especificação.

RED · 1Spec do Controller via supertest
Começamos pelo topo: o uso real via HTTP
src/controllers/userController.spec.ts

🔴 Resultado esperado

Falha. Não existe app ainda, nem rota, nem controller. Isso é certo.

🧠 Por que começar por aqui?

Esse spec descreve como o sistema é realmente usado: uma chamada HTTP. Quando ele passar, sabemos que o caminho controller → service → repository está fechado.

RED · 2Spec do Service mockando o Repository
Isolamos a regra de negócio — sem tocar no banco
src/services/userService.spec.ts

🎭 Test doubles

  • mock — função fake controlada por nós.
  • jest.fn() — cria a função.
  • mockResolvedValue() — define o que a Promise resolve.

🔴 O que cobrimos aqui

  • Caminho feliz: repo retorna user → service devolve.
  • Caminho infeliz: repo retorna null → service lança NotFoundError.
RED · 3Spec do Repository — SQL parametrizado
Aqui o teste mostra que a query é parametrizada e atinge o pool
src/repositories/userRepository.spec.ts

💾 Banco efêmero — uma nota

Aqui mockamos o pool.query. Quando precisarmos de um banco real (próximas aulas), usaremos um banco efêmero — Postgres em container, criado e derrubado a cada suíte.

🔴 O que provamos

  • Que a query usa placeholder $1 (anti-injeção).
  • Que o pool é chamado com o id recebido.
  • Que retornamos null quando não há linhas.
RED · 4Spec do Helper puro — isValidEmail
Função pura: entrada → saída, sem efeitos colaterais
src/helpers/email.spec.ts

🎯 Matchers comuns

  • toBe — igualdade estrita (===).
  • toEqual — igualdade profunda de objetos/arrays.
  • toMatchObject — subset de propriedades.
  • rejects.toThrow — Promise rejeitada.

♻️ Função pura

Helpers puros são o caso mais simples de testar — comece por eles para aquecer e termine por eles para refatorar com segurança.

📁 Estrutura — .spec.ts ao lado de cada arquivo
MVC com 6 camadas — cada camada com seu teste vizinho
src/ ├── controllers/ │ ├── userController.ts │ └── userController.spec.ts ├── routes/ │ └── userRoutes.ts ├── services/ │ ├── userService.ts │ └── userService.spec.ts ├── repositories/ │ ├── userRepository.ts │ └── userRepository.spec.ts ├── models/ │ └── user.ts // interface User ├── helpers/ │ ├── email.ts │ └── email.spec.ts ├── middlewares/ │ └── errorHandler.ts ├── errors/ │ └── AppError.ts ├── db/ │ └── pool.ts ├── app.ts └── server.ts

🏗️ As 6 camadas

🛣️
routes
URL → controller
🎮
controllers
HTTP in/out, sem regra
🧠
services
regras de negócio
🗄️
repositories
SQL e pool
📐
models
interfaces (contratos)
🛠️
helpers
funções puras
GREEN · 1Controller e Rotas — sempre primeiro
É a casca: amarra HTTP ao service. Mínimo possível para o teste passar.
src/controllers/userController.ts
src/routes/userRoutes.ts + app.ts

🟢 Casca montada

O teste do controller ainda falha (falta o service), mas a estrutura está pronta. Agora descemos uma camada.

GREEN · 2Service — a regra de negócio
Pega no repository, decide o que fazer com a resposta
src/services/userService.ts

📐 Model — interface User

export interface User {
  id: number;
  name: string;
  email: string;
}

🟢 Resultado

Os dois testes do service passam. O do controller ainda depende do repository — vamos lá.

GREEN · 3Repository + Pool — SQL parametrizado
$1 sempre · nunca concatenar string com input do usuário
src/repositories/userRepository.ts
src/db/pool.ts

⚠️ Anti-injeção

Nunca: "SELECT * FROM users WHERE id = " + req.params.id. Sempre: pool.query("... WHERE id = $1", [id]). O driver do pg escapa o valor automaticamente.

GREEN · 4Helpers + Errors + Middleware
Erros de domínio com classe própria — middleware traduz para HTTP
src/errors/AppError.ts
src/middlewares/errorHandler.ts
src/helpers/email.ts

✅ Tudo verde

Os 4 specs passam. npm test retorna verde. Agora podemos refatorar.

RM-ODP — As 5 Visões do Sistema
ISO/IEC 10746 · As 5 visões aplicadas ao MVC do back-end tipado
🏢 Enterprise
Propósito, regras de negócio
📋 Information
Tipos, interfaces, modelos
⚙️ Computational
Controller, service, repo
🔧 Engineering
Pool, middleware, jest
💻 Technology
Node, TS, pg, supertest

Esta aula: Visão Computational — desenhamos a casca do back-end (controller → service → repository) com TDD top-down.

Esta Aula: Computational
RM-ODP · RF, RNF e Artefato — 8 Eixos no back tipado

📌 Requisito Funcional

Expor a casca dos endpoints do recurso central do projeto (controller → service → repository) verificada por testes automatizados.

📦 Artefato

  • 🧪 Specs Jest + supertest (RED)
  • 🟢 Implementação mínima que faz passar
  • ♻️ Refatoração com cobertura instalada

⚖️ RNF — 8 Eixos ISO/IEC 25010

CONF Confiabilidade✅ Testes automáticos
MANT Manutenibilidade✅ TS + camadas isoladas
DES Desempenho→ pool reusado
SEG Segurança→ validação no controller
SUP Suportabilidade→ specs documentam
REFACTOR♻️ → RM-ODP → encerramento
Com testes verdes, podemos extrair, renomear e reorganizar com segurança
🔴
RED
Spec falha — descreve a intenção
🟢
GREEN
Mínimo código para passar
♻️
REFACTOR
Limpa, extrai, renomeia — testes seguram

♻️ O que refatoramos hoje

  • Extrair asyncHandler para tirar o try/catch dos controllers.
  • Mover validações para helpers/ (testáveis isoladamente).
  • Tipar callbacks Express com RequestHandler.

📊 Coverage — métrica, não meta

Use cobertura para encontrar buracos, não para perseguir 100%. Um teste sem asserção real não cobre nada — só engana o relatório.

🏢 Enterprise
Propósito do recurso
📋 Information
interface User
⚙️ Computational
controller/service ◀ aqui
🔧 Engineering
pool, middleware
💻 Technology
Node, TS, pg

🚀 Próxima aula — Back-End II

POST/PUT/DELETE com validação, tratamento robusto de erros e o CRUD HTTP completo da entidade do projeto.

🎯 Desafio — Conectar ao Supabase e rodar a migration
Clone o projeto base, configure a DATABASE_URL do Supabase e materialize a modelagem física no Postgres

📥 1. Baixar o projeto e ler o CHANGELOG.md

Clone, instale dependências e leia o CHANGELOG.md com calma, ponto a ponto — ele descreve a estrutura do projeto e o que cada migration faz.

git clone https://git.inteli.edu.br/afonso.brandao/modulo2in.git
cd modulo2in
npm install
more CHANGELOG.md   # leia entrada por entrada antes de seguir

🔑 2. Pegar a URL do banco no Supabase

  • No painel do projeto: Project Settings → Database → Connection string → URI.
  • Use o pooler (pgbouncer) na porta 6543 para apps Node.
  • Substitua [YOUR-PASSWORD] pela senha real do banco.
DATABASE_URL=postgresql://postgres.[REF]:[SENHA]@aws-0-[REGION].pooler.supabase.com:6543/postgres

⚙️ 3. Configurar o .env

Crie um .env na raiz do projeto com a URL acima. O pool em src/db/pool.ts já lê process.env.DATABASE_URL.

# .env
DATABASE_URL=postgresql://postgres.[REF]:[SENHA]@...:6543/postgres

🛠️ 4. Rodar a migration

Aplique a modelagem física definida na pasta migrations/ diretamente no Postgres do Supabase.

npm run migrate
# verifique no Supabase: Table Editor mostra as tabelas criadas

✅ Definition of Done

  • Repositório clonado e npm install sem erros.
  • .env com DATABASE_URL apontando para o Supabase do seu projeto.
  • Migration executada — tabelas visíveis no Supabase Table Editor.
  • Conexão validada pelo back-end (sem erro de autenticação ao subir o servidor).