ADRIANOLAUREANO← Artigos
SÉRIE PROVA · CAPÍTULO 1 · RASCUNHO

O que acontece quando executamos os testes?

Construindo um test runner do zero em Rust — da descoberta das funções Pulso ao relatório do Prova 0.1.

Pergunta de abertura

Quem encontra cada teste, impede uma falha de encerrar a suíte e ainda sabe apontar arquivo, linha, esperado e recebido?

01
O botão verde esconde um pequeno sistema.
test é o começo, não o trabalho

Uma função de teste é código comum. O que muda são as regras ao redor dela: alguém precisa encontrá-la, preparar um contexto, observar sua saída e transformar o término em resultado. Esse coordenador é o runner.

prova test
    ├─ descobrir ─► catálogo
    ├─ executar  ─► eventos
    └─ relatar   ─► pessoa + CI + exit code
O relatório consome eventos; ele não executa testes.
PeçaResponsabilidade
Casounidade executável com nome e origem
Suítecoleção planejada
Assertioncomparação com contexto
Runnerplanejamento e execução
Frameworkcontratos, lifecycle e integrações
02
Antes de executar, precisamos saber o que existe.
Descoberta não exige reflexão

Linguagens usam nomes, diretórios, metadados, macros, reflexão ou registro. A Pulso ganha a declaração teste sem quebrar programas existentes.

teste "soma dois inteiros" {
  assert_eq(soma(2, 2), 4)
}

O parser preserva nome, arquivo, linha e coluna. O compilador emite bytecode Cadência e guarda metadados num catálogo separado.

suite.pulso
  ├─ soma dois inteiros @ 3:1 ─► função #7
  └─ consulta perfil   @ 8:1 ─► função #9 · ignored
03
O caso atravessa o compilador e entra numa execução observada.
Fonte → AST → bytecode → VM

Reutilizamos lexer, parser e AST da Pulso e o bytecode da Cadência. O Crivo diagnostica antes da execução; lint não vira teste.

fonte + posição ─► AST TestDecl ─► função Cadência
                                      ↓
catálogo ─► filtro ─► VM nova ─► eventos
04
Falhar um caso não derruba a suíte.
Um contexto Cadência por teste

O Prova cria stack, globais, heap e stdout novos por caso. Isso não é um processo novo: é isolamento dentro do runtime controlado.

Passou
Falhou(FalhaAssertion)
ErroRuntime(ErroCadencia)
Ignorado(Motivo)
ErroRunner(FalhaInterna)

Timeout será uma categoria própria no capítulo 3. Falha do programa e falha do worker nunca são misturadas.

05
Uma assertion útil carrega mais do que falso.
Expressão, valores e origem

Uma função que recebe apenas 5 e 4 perdeu as expressões originais. O frontend Pulso reconhece as assertions internas e anexa expressões e posições ao bytecode.

FALHOU soma dois inteiros
suite.pulso:12:5
assert_eq(soma(2, 3), 4)
  esperado: 4
  recebido: 5

Macros resolvem isso de outra forma em Rust. O Prova declara sua escolha em vez de fingir que todas as linguagens funcionam igual.

06
Comparar também é decidir como explicar.
Valores e diffs legíveis

assert, assert_eq e assert_ne usam igualdade estrutural. Strings recebem diff por linhas e listas apontam o primeiro índice diferente. Cor é apoio, nunca a única pista.

esperado: "linha azul"
recebido: "linha verde"
                 -----  +++++
07
O runner emite fatos; o relatório escolhe a forma.
Saída, duração e ordem

Cada VM escreve num buffer. Um relógio monotônico mede duração. SuiteStarted, CaseStarted, Stdout, CaseFinished e SuiteFinished desacoplam execução de apresentação.

$ prova list
suite.pulso:3 soma dois inteiros
$ prova test soma
PASSOU soma dois inteiros 0,31 ms
1 caso · 1 passou · 0 falhou

Falha, erro de runtime ou erro interno produzem exit code diferente de zero para shell e CI.

08
Agora vemos um teste inteiro.
Sem saltos invisíveis
  1. O parser preserva a posição.
  2. O compilador cria função e mapa de fonte.
  3. O catálogo registra identidade e tags.
  4. O filtro seleciona sem alterar identidade.
  5. Uma VM nova executa.
  6. A assertion cria falha estruturada.
  7. O runner encerra o contexto e emite evento.
  8. O relatório resume e escolhe o exit code.
09
Prova 0.1 é funcional, não industrial.
O primeiro estado verificável

Ele descobre fixtures próprias, filtra, executa, continua após falha, captura saída e relata. Não possui reflexão, plugins, processo por caso nem compatibilidade total com cargo test, JUnit, pytest, Jest ou Vitest.

No próximo capítulo, um teste que passa sozinho revelará por que uma VM nova ainda não controla arquivos, tempo e serviços.

Baixar Prova 0.1 — estado exato deste capítulo

PROJETO COMPLETO · ESTADO EXATO DESTE CAPÍTULO

Baixe o Prova 0.1.

Pacote exclusivo com código Rust e fixtures Pulso. Nenhum download anterior é substituído.

O que este artigo fez você pensar?

Dúvidas, experiências e contrapontos ajudam a próxima pessoa a enxergar o assunto por outro ângulo.

Todos passam por moderação. Ao enviar, você concorda com a política de privacidade.

Receba os próximos artigos.

Uma mensagem quando uma nova investigação estiver pronta. Só isso.