Da especificação para o código
Nesta aula
A frase precisa de uma consequência observável
Um requisito descreve uma necessidade. Um contrato descreve o comportamento que uma unidade de software deve cumprir: quais entradas recebe, quais saídas produz e o que acontece quando a entrada não é aceitável. O código implementa esse acordo; o teste executa um caso e compara o resultado com uma expectativa definida antes da solução. A passagem entre esses elementos exige decisões explícitas, não uma tradução automática de português para JavaScript.
Na Oficina Caracol, REQ-002 diz que o catálogo público não revela registros inativos. A consequência alcança o servidor: esconder um cartão na página seria insuficiente se a resposta de rede ainda contivesse o registro. A lista pública precisa ser filtrada antes de atravessar essa fronteira. Os dados usados nos exercícios são fictícios. A implementação didática não precisa de cadastro, banco externo ou biblioteca adicional.
Contrato preenchido de uma unidade
| Campo | Decisão da Oficina |
|---|---|
| Necessidade | REQ-001 lista serviços ativos; REQ-002 exclui inativos. |
| Entrada | Lista de casos válida conforme a validação do projeto. |
| Regra | Somente registros cujo campo ativo é o booleano true. |
| Saída | Nova lista com os casos ativos; lista vazia é um resultado válido. |
| Entrada inválida | Rejeição explícita na validação; não converter erro de dado em catálogo vazio. |
| Preservação | Não alterar a lista recebida; não acrescentar dependências. |
| Prova mínima | Caso ativo, inativo, vazio e inválido; depois verificar a rota pública. |
Booleano é um tipo com dois valores, true e false. A string "false" contém texto e não significa o booleano false. Por isso, o contrato de dados precisa impedir ambiguidades. A função de seleção trabalha depois da validação do conjunto; não cabe inventar silenciosamente um significado para registros malformados.
Procedimento para construir a primeira prova
- Escolha um requisito e escreva ao menos um exemplo que deve passar e um que deve ser recusado.
- Localize a função e os testes existentes antes de modificar arquivos. Preserve os contratos já utilizados por outros módulos.
- Escreva a expectativa do teste antes da implementação. A falha inicial precisa apontar o comportamento ausente, não um import com nome errado.
- Implemente a menor alteração que atende ao contrato. Execute npm test e npm run validate dentro de pratica.
- Compare o diff com o escopo e realize a integração com a rota e a página. Registre versão, ambiente, comando, resultado e limite.
Um exemplo de regra e asserção
O código abaixo é o recorte real da seleção em src/catalogo.mjs. validarCasos rejeita tipos, campos e identificadores inválidos; camposPublicos cria um objeto com somente os três campos que podem sair do servidor. O conjunto validado contém booleanos, por isso a condição caso.ativo seleciona precisamente os true.
function camposPublicos({id, titulo, descricao}) {
return {id, titulo, descricao};
}
export function listarAtivos(casos) {
validarCasos(casos);
return casos.filter(caso => caso.ativo).map(camposPublicos);
}filter devolve uma nova lista com os elementos que satisfazem a condição. map transforma cada selecionado no formato público. A desestruturação de camposPublicos dá nomes aos campos recebidos, e o novo objeto exclui ativo. Não copiar campos extras reduz o risco de publicar informação que não pertence ao contrato. O teste seguinte usa os exports reais e o mesmo caminho de importação de tests/catalogo.test.mjs.
import test from 'node:test';
import assert from 'node:assert/strict';
import { listarAtivos, validarCasos } from '../src/catalogo.mjs';
test('seleção cumpre o contrato público', () => {
const entrada = [
{ id: 'revisao', titulo: 'Revisão', descricao: 'Exemplo A', ativo: true },
{ id: 'rascunho', titulo: 'Rascunho', descricao: 'Exemplo B', ativo: false }
];
const antes = structuredClone(entrada);
assert.deepEqual(listarAtivos(entrada), [
{ id: 'revisao', titulo: 'Revisão', descricao: 'Exemplo A' }
]);
assert.deepEqual(entrada, antes);
assert.deepEqual(listarAtivos([]), []);
assert.throws(
() => validarCasos([{ ...entrada[0], ativo: 'true' }]),
TypeError
);
});deepEqual compara a estrutura esperada; structuredClone guarda uma cópia para verificar preservação da entrada. throws exige uma rejeição do tipo declarado. Essas asserções cobrem seleção, projeção, vazio, preservação e um caso inválido. Ainda não provam leitura de arquivo, resposta HTTP ou comportamento no navegador. Reúna os imports uma vez se incorporar o teste ao módulo existente; não substitua a suíte completa por esse recorte.
Erros que mudam o sentido do requisito
| Erro | Reparo específico |
|---|---|
| Filtrar somente na interface | Verificar a resposta pública e retirar inativos antes de enviá-la. |
| Aceitar "true" como se fosse true | Validar tipos e mostrar o diagnóstico de dado inválido. |
| Retornar [] para qualquer exceção | Distinguir conjunto legitimamente vazio de falha de leitura ou validação. |
| Afirmar integração após teste de função | Executar também o caminho servido pela aplicação. |
O avanço desta unidade exige verificações aplicáveis aprovadas, preservações confirmadas e ausência de lacuna impeditiva. Isso sustenta M5 para o conjunto explicitamente testado. M6 requer evidência do fluxo integrado; a asserção isolada não autoriza esse estado.
Exercício com resolução
Entrada fornecida: A possui ativo true; B possui ativo false; C possui ativo true. Escreva os identificadores esperados, depois altere B para a string "true". O conjunto continua válido? Qual resultado deve ser mostrado ao usuário se a validação rejeitar o arquivo?
Ver solução comentada
Resolução: a primeira lista pública contém A e C, nessa ordem. A string torna o segundo conjunto inválido segundo o contrato booleano; não se deve publicar B nem chamar a falha de catálogo vazio. A interface deve mostrar o estado explícito de indisponibilidade previsto pelo projeto, preservando a navegação. A rubrica exige separar seleção e validação, indicar o ponto da rejeição e incluir uma asserção sobre inativos. Uma resposta que apenas imprime A e C cobre somente o primeiro caso.
Leitura técnica relacionada
S18: MDN, Array.prototype.filter, seções Syntax e Description, para a seleção e a criação de uma nova lista. S19: Node.js, módulo node:test e asserções, para executar expectativas. A ligação entre requisito, contrato e evidência apresentada aqui é a organização didática do método.
Fontes relacionadas
Consulte os limites e as seções de apoio de cada referência.
S18 S19Tente o exercício antes de consultar a solução. Marque a aula para organizar sua leitura.
A conclusão desta leitura não equivale a certificação ou aceite de um projeto real. Teste os exemplos no escopo indicado e registre as limitações.