# Aula 3.6 - Testes, documentação e projeto final

## Identificação

- **Duração:** 2 horas
- **Tipo:** oficina integradora
- **Entrega:** projeto TypeScript testado e documentado
- **Laboratório:** [`../exemplos/aula-3.6/index.html`](../exemplos/aula-3.6/index.html)

## Introdução

### O que é um teste de software?

Um teste executa uma parte do sistema sob condições conhecidas e compara o
resultado observado com o resultado esperado.

```ts
const result = calculateCompletionRate(2, 4);
assert.equal(result, 50);
```

O teste registra uma expectativa verificável. Se o resultado mudar para `40`, a
verificação falha e mostra onde investigar.

### Testar é somente abrir a página e clicar?

Clicar manualmente é uma forma de teste e continua sendo útil. Porém, um teste
automatizado pode repetir os mesmos cenários rapidamente, de forma consistente e
sem depender da memória de quem testa.

Os dois se complementam:

- teste automatizado verifica regras repetíveis;
- teste manual e exploratório encontra comportamentos inesperados e problemas de
  experiência;
- revisão visual verifica layout, legibilidade e adaptação a telas.

### Um teste aprovado prova que não existem bugs?

Não. Ele prova somente que os cenários descritos produziram o resultado esperado
naquele ambiente. Se um caso importante não foi escrito, a suíte não o verifica.

Cobertura alta também não é garantia de qualidade. É possível executar muitas
linhas sem avaliar corretamente o comportamento.

### O que é documentação?

Documentação é informação que permite entender, executar, utilizar, manter e
justificar o projeto. Para esta etapa, o principal documento será o `README.md`.

Um README não deve dizer apenas “projeto de tarefas”. Ele deve responder:

- qual problema o projeto resolve;
- quais tecnologias utiliza;
- como instalar e executar;
- como rodar verificações e testes;
- como a estrutura está organizada;
- quais decisões e limitações existem.

### README é documentação do código?

É uma parte. Comentários, nomes, tipos, contratos, diagramas, decisões de arquitetura
e documentação de API também comunicam o sistema. O README funciona como porta de
entrada.

### O que é um projeto reproduzível?

É um projeto que outra pessoa consegue preparar e executar seguindo instruções
registradas, usando versões e comandos previsíveis.

```bash
npm ci
npm run quality
npm run build
```

Se somente o autor sabe quais passos secretos executar, a entrega ainda não está
completa.

### Onde os testes rodam?

Podem rodar:

- na máquina do desenvolvedor;
- pelo editor;
- antes de um commit;
- no fluxo de integração contínua;
- durante a compilação;
- em um navegador, para testes de interface.

Nesta aula veremos testes unitários de regras TypeScript e um laboratório visual no
navegador.

### Como isso fecha o módulo 3?

As aulas anteriores produziram tipos, contratos, unions, configuração, qualidade e
fluxo Git. Agora vamos reuni-los em uma entrega que outra pessoa consegue instalar,
verificar, revisar e continuar.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar o propósito de um teste;
2. diferenciar testes manual e automatizado;
3. diferenciar unidade, integração e E2E;
4. estruturar testes com Arrange, Act e Assert;
5. testar casos felizes, limites e falhas;
6. testar comportamento sem acoplar à implementação;
7. interpretar uma falha;
8. explicar limites de cobertura e mocks;
9. organizar arquivos de teste;
10. escrever um README reproduzível;
11. documentar decisões e limitações;
12. entregar o projeto final do módulo.

## Pré-requisitos

- Aulas 3.1 a 3.5 concluídas;
- funções, módulos, TypeScript estrito e scripts npm;
- noções de Git, branches e pull requests.

## Pergunta orientadora

> Como entregar um projeto que outra pessoa consiga compreender, executar, verificar e modificar com segurança?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Fundamentos de testes | 25 min |
| Testes unitários TypeScript | 30 min |
| Estratégia e interpretação | 20 min |
| Documentação reproduzível | 20 min |
| Laboratório e entrega final | 20 min |
| Revisão | 5 min |

## 1. Pirâmide de testes

Uma estratégia comum organiza:

- muitos testes unitários rápidos;
- alguns testes de integração;
- poucos testes E2E para fluxos essenciais.

Não é uma lei matemática. A distribuição depende dos riscos, arquitetura e custo de
manutenção.

## 2. Teste unitário

Verifica uma unidade pequena e controlável, normalmente uma função ou regra:

```ts
export function calculateCompletionRate(
  completed: number,
  total: number,
): number {
  if (total === 0) return 0;
  return Math.round((completed / total) * 100);
}
```

Esse código pode ser testado sem navegador, banco ou rede.

## 3. Teste de integração

Verifica componentes trabalhando juntos. Exemplos:

- serviço e repositório;
- controller e validação;
- aplicação e MySQL de teste;
- cliente HTTP e API controlada.

Uma integração real pode exigir preparação e limpeza de dados.

## 4. Teste E2E

End-to-end verifica um fluxo pela interface externa do sistema:

```text
usuário entra → cria tarefa → filtra → vê resultado
```

Ele oferece confiança no fluxo integrado, mas costuma ser mais lento e sensível ao
ambiente.

## 5. Arrange, Act, Assert

```ts
test("calcula 50% de conclusão", () => {
  // Arrange
  const completed = 2;
  const total = 4;

  // Act
  const result = calculateCompletionRate(completed, total);

  // Assert
  assert.equal(result, 50);
});
```

- Arrange prepara;
- Act executa;
- Assert compara.

Separar mentalmente essas etapas melhora a leitura.

## 6. Nome do teste

Um nome útil descreve comportamento e condição:

```text
retorna zero quando não existem tarefas
rejeita total negativo
preserva somente tarefas concluídas
```

Evite `teste 1`, `funciona` ou nomes que apenas repetem a função.

## 7. Caso feliz

É o uso válido e esperado:

```ts
assert.equal(calculateCompletionRate(3, 4), 75);
```

Ele é necessário, mas raramente suficiente.

## 8. Casos de limite

Valores nas bordas revelam erros:

- array vazio;
- zero;
- um item;
- string vazia;
- limite máximo;
- data exatamente no vencimento.

```ts
assert.equal(calculateCompletionRate(0, 0), 0);
```

## 9. Casos inválidos

O contrato deve decidir o que acontece:

```ts
assert.throws(
  () => calculateCompletionRate(-1, 3),
  /não pode ser negativo/,
);
```

Não escreva apenas um teste de erro; teste também a mensagem ou categoria quando
ela faz parte do contrato público.

## 10. Testar comportamento

Prefira testar a saída pública:

```ts
assert.deepEqual(filterTasks(tasks, "done"), [doneTask]);
```

Evite afirmar detalhes internos como quantidade de loops ou nomes de variáveis. Uma
refatoração correta não deveria quebrar testes de comportamento.

## 11. Testes determinísticos

O mesmo teste deve produzir o mesmo resultado nas mesmas condições. Fontes comuns
de instabilidade:

- hora atual;
- aleatoriedade;
- rede externa;
- ordem não garantida;
- dados compartilhados;
- concorrência sem controle.

Injete relógio, gerador ou adaptador quando precisar controlar essas dependências.

## 12. Testes independentes

Um teste não deve depender da execução anterior. Cada caso prepara seus próprios
dados e limpa recursos quando necessário.

Se mudar a ordem altera o resultado, existe estado compartilhado indevido.

## 13. Node Test Runner

O Node possui um executor de testes integrado:

```ts
import test from "node:test";
import assert from "node:assert/strict";

test("retorna zero para coleção vazia", () => {
  assert.equal(calculateCompletionRate(0, 0), 0);
});
```

Com JavaScript executável:

```bash
node --test
```

Em um projeto TypeScript, é necessário compilar ou usar uma configuração de ambiente de execução
compatível com TypeScript.

## 14. Organização dos arquivos

Alternativas:

```text
src/task-rules.ts
src/task-rules.test.ts
```

ou:

```text
src/task-rules.ts
tests/task-rules.test.ts
```

Escolha uma convenção e configure `tsconfig`, lint e executor de forma coerente.

## 15. Testes parametrizados

```ts
const cases = [
  { completed: 1, total: 4, expected: 25 },
  { completed: 2, total: 4, expected: 50 },
  { completed: 4, total: 4, expected: 100 },
];

for (const item of cases) {
  test(`${item.completed}/${item.total}`, () => {
    assert.equal(
      calculateCompletionRate(item.completed, item.total),
      item.expected,
    );
  });
}
```

São úteis quando a mesma regra precisa ser verificada com várias entradas.

## 16. Teste assíncrono

```ts
test("carrega tarefas", async () => {
  const tasks = await service.list();
  assert.equal(tasks.length, 2);
});
```

Retorne ou aguarde a Promise. Esquecer `await` pode fazer o teste terminar antes da
verificação.

## 17. Dublês de teste

Termos comuns:

- stub: fornece resposta controlada;
- spy: registra chamadas;
- mock: simula interação esperada;
- fake: implementação simplificada funcional.

Os nomes variam entre ferramentas. O mais importante é saber qual dependência está
sendo substituída e por quê.

## 18. Quando usar mock

Use para controlar uma fronteira difícil, lenta ou externa, como pagamento, e-mail
ou modelo de IA. Não faça mock de toda função interna; isso cria testes que repetem
a implementação sem provar o comportamento integrado.

## 19. Cobertura

Cobertura mede quais partes foram executadas pela suíte. Ela ajuda a encontrar áreas
sem exercício, mas não mede a qualidade das afirmações.

Um teste sem assert pode executar 100% da função e provar quase nada.

## 20. Falha de teste

Ao ler uma falha, identifique:

1. nome do teste;
2. valor esperado;
3. valor recebido;
4. stack trace;
5. primeira linha do projeto envolvida;
6. se código ou expectativa está incorreto.

Não altere o valor esperado apenas para deixar a suíte verde.

## 21. Regressão

Quando um defeito é corrigido:

1. crie um teste que reproduz a falha;
2. confirme que ele falha;
3. corrija o código;
4. confirme que passa;
5. mantenha o teste para evitar retorno do problema.

## 22. Testes e TypeScript

O compilador verifica contratos estáticos; testes verificam exemplos de
comportamento em ambiente de execução. Precisamos dos dois:

```text
tsc --noEmit → tipos
node --test   → comportamento
```

TypeScript não prova que `calculateCompletionRate(2, 4)` retorna `50`.

## 23. Porta de qualidade final

```json
{
  "scripts": {
    "typecheck": "tsc --noEmit",
    "lint": "eslint .",
    "format:check": "prettier . --check",
    "test": "node --test dist/**/*.test.js",
    "build": "tsc",
    "quality": "npm run typecheck && npm run lint && npm run format:check && npm test"
  }
}
```

O comando exato depende da saída e da versão do Node.

## 24. O README como porta de entrada

Estrutura recomendada:

```md
# Nome do projeto

## Problema e objetivo
## Funcionalidades
## Tecnologias
## Pré-requisitos
## Instalação
## Execução
## Testes e qualidade
## Estrutura
## Decisões técnicas
## Limitações
## Licença
```

Nem todo projeto precisa das mesmas seções, mas instalação e execução não devem
ficar implícitas.

## 25. Pré-requisitos claros

Exemplo:

```md
- Node.js 22 ou superior;
- npm 10 ou superior;
- navegador atualizado.
```

Não escreva apenas “ter Node”. Versões incompatíveis podem gerar erros diferentes.

## 26. Comandos copiáveis

```bash
npm ci
npm run build
npm test
```

Informe de qual diretório executar e quais arquivos de ambiente preparar. Não
inclua credenciais reais.

## 27. Variáveis de ambiente

Documente nomes em `.env.example`:

```text
API_BASE_URL=http://localhost:3000
```

Explique finalidade e obrigatoriedade. Nunca coloque tokens reais no README ou no
arquivo de exemplo.

## 28. Decisões e limitações

Exemplo:

```md
## Decisões técnicas

- TypeScript estrito para contratos mais precisos;
- regras de domínio separadas do DOM para facilitar testes.

## Limitações

- dados ainda não possuem persistência no servidor;
- autenticação será adicionada em módulo posterior.
```

Limitação documentada é diferente de defeito escondido.

## 29. Documentação que envelhece

README incorreto pode ser pior que ausência de instrução. Quando scripts, versões ou
estrutura mudarem, atualizar a documentação na mesma entrega.

Comandos do README devem ser testados em ambiente limpo sempre que possível.

## 30. Laboratório guiado

Abra o [painel de entrega](../exemplos/aula-3.6/index.html).

### Etapa 1 — Execute a suíte

Alterne entre implementação correta e implementação com regressão. Observe qual
teste identifica o problema.

### Etapa 2 — Leia cada caso

Relacione Arrange, Act e Assert. Identifique caso feliz, limite e entrada inválida.

### Etapa 3 — Audite o README

Use o README completo e depois remova uma seção. O painel deve explicar o que falta,
sem avaliar apenas o tamanho do texto.

### Etapa 4 — Verifique a entrega

Uma entrega fica pronta apenas quando testes e documentação atendem aos critérios.

## 31. Projeto final do módulo

Migrar o painel de tarefas para TypeScript e entregar:

- entidades e estados tipados;
- validação de dados externos;
- TypeScript estrito;
- módulos de regras separados da interface;
- testes de casos felizes, limites e falhas;
- lint e formatação;
- porta de qualidade;
- branch de funcionalidade e commits coerentes;
- pull request simulado ou real;
- README reproduzível;
- demonstração de cinco minutos.

## 32. Estrutura sugerida do projeto

```text
painel-typescript/
├── src/
│   ├── domain/
│   ├── services/
│   ├── ui/
│   └── app.ts
├── tests/
├── dist/
├── .gitignore
├── eslint.config.js
├── package.json
├── package-lock.json
├── README.md
└── tsconfig.json
```

## 33. Evidências da entrega

- captura ou registro da porta de qualidade;
- histórico de commits;
- branch utilizada;
- descrição do pull request;
- lista de testes;
- instruções reproduzidas por outra pessoa;
- demonstração do fluxo principal;
- explicação de uma decisão técnica.

## 34. Erros comuns

### Testar somente o caso feliz

Arrays vazios, limites e entradas inválidas ficam sem proteção.

### Testar implementação interna

Refatorações corretas quebram a suíte sem alterar comportamento.

### Criar mocks demais

O teste passa mesmo que os componentes reais não funcionem juntos.

### Perseguir cobertura sem asserts relevantes

Linhas executadas não significam comportamento validado.

### README que só funciona na máquina do autor

Faltam versões, variáveis, diretório ou etapas.

### Documentação diferente dos scripts

O leitor copia um comando que não existe mais.

### Desativar o teste com falha

A causa permanece escondida. Investigue antes de pular.

## 35. Boas práticas

- teste comportamento público;
- inclua casos de limite;
- mantenha testes determinísticos e independentes;
- nomeie casos por comportamento;
- use dublês somente em fronteiras justificadas;
- execute testes durante o desenvolvimento;
- transforme defeitos em testes de regressão;
- mantenha README e scripts sincronizados;
- documente decisões e limitações;
- peça para outra pessoa seguir as instruções.

## 36. Exercícios

### Exercício 1 — AAA

Escreva um teste com Arrange, Act e Assert para uma função de prioridade.

### Exercício 2 — Limites

Crie três casos para array vazio, um item e muitos itens.

### Exercício 3 — Regressão

Introduza um defeito controlado e escreva o teste que o encontra.

### Exercício 4 — README

Entregue o projeto a um colega usando apenas o README.

### Exercício 5 — Evidências

Registre o resultado de `npm run quality` sem incluir dados sensíveis.

## 37. Lista de verificação de conclusão

- [ ] Diferencio teste manual e automatizado.
- [ ] Diferencio unidade, integração e E2E.
- [ ] Estruturo Arrange, Act e Assert.
- [ ] Testo caso feliz, limite e falha.
- [ ] Testo comportamento público.
- [ ] Entendo limites de mocks e cobertura.
- [ ] Interpreto falhas sem alterar expectativas arbitrariamente.
- [ ] Escrevo README reproduzível.
- [ ] Documento decisões e limitações.
- [ ] Executo a porta de qualidade antes da entrega.

## 38. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Modelagem TypeScript | 20 |
| Testes e cenários | 25 |
| Configuração e qualidade | 15 |
| Organização do projeto | 10 |
| Fluxo Git e revisão | 10 |
| README reproduzível | 15 |
| Demonstração e justificativa | 5 |
| **Total** | **100** |

## 39. Critérios de aceite

- pelo menos 70 pontos;
- projeto executável seguindo o README;
- TypeScript estrito sem `any` usado para esconder erros;
- porta de qualidade aprovada;
- regras essenciais testadas;
- nenhum segredo versionado;
- branch e commits demonstráveis;
- limitações conhecidas documentadas.

## Resumo

Nesta aula, aprendemos que:

- testes transformam expectativas em verificações repetíveis;
- teste aprovado não prova ausência de todos os bugs;
- unidade, integração e E2E protegem riscos diferentes;
- AAA organiza a intenção do caso;
- limites e falhas importam tanto quanto o caso feliz;
- cobertura é um indicador, não um objetivo isolado;
- README é a porta de entrada da entrega;
- projeto reproduzível não depende de passos secretos;
- código, testes, configuração, Git e documentação formam uma única entrega.

## Próximo módulo

No Módulo 4, iniciaremos Angular e transformaremos as regras e contratos construídos
até aqui em uma aplicação front-end organizada por componentes, serviços e rotas.
