Testes
Tudo sobre testar em DataForge: o corredor, os matchers, dublês, propriedades, cobertura e CI.
Testar não é uma biblioteca que se instala: crucible, trial e expect são palavras da linguagem, e dataforge test já está no executável. Não há dependência, não há configuração, e o arquivo de teste é um .df comum.
adopt Arcane.Crucible as C
action somar(a, b):
yield a + b
crucible "soma":
trial "soma dois numeros":
expect somar(2, 3) is 5
trial "aceita decimais":
expect somar(0.5, 0.25) is 0.75
C.run()$ dataforge test tests/
✓ tests/soma_test.df (2/2)
2 passaram em 1 arquivo(s) — 0.03s
Tudo verde.O caminho inteiro#
Crucible
o corredor: crucible, trial, expect e como ele descobre os arquivos
Os matchers
is, contains, matches, throws — e o que cada um relata ao falhar
Fixtures e ganchos
antes, depois, e o estado que não pode vazar entre testes
Dublês
substituir o que é lento, caro ou de fora
Teste por propriedade
milhares de entradas, e a menor que quebra
Relatórios e CI
JUnit, cobertura mínima e o código de saída
Cobertura
os dois jeitos de o número mentir
Instantâneos
saída grande, e banco isolado por teste
Testar uma biblioteca
importar pelo nome público, e não pelo caminho
Onde os testes moram#
| Padrão | Encontrado por |
|---|---|
tests/ | dataforge test |
*_test.df em qualquer lugar | dataforge test |
[scripts] test = … no forge.toml | dataforge test sem argumento |
forge_modules/ | ignorado de propósito |
A última linha é uma correção: um projeto com 13 testes relatava 89, e a suíte ficava vermelha por falha de uma biblioteca que ninguém daquele projeto escreveu.
As quatro perguntas que um teste responde#
| Pergunta | Forma |
|---|---|
| dá o valor certo? | expect f(x) is esperado |
| falha quando deve? | expect f(ruim) throws ValidationError |
| mudou o que não devia? | expect saida matches snapshot |
| vale para qualquer entrada? | teste por propriedade |
A segunda é a mais esquecida, e a que mais rende: um código que só é testado no caminho feliz costuma ter a mensagem de erro errada — ou nenhuma.
Um teste que reprova de verdade#
Hoje há um resultado por trial, e não por arquivo: "1 de 2 falhou" sem dizer qual não serve para nada.
Estado entre testes#
crucible "carrinho":
setup:
carrinho := [] // roda antes de CADA trial
trial "comeca vazio":
expect len(carrinho) is 0
trial "aceita um item":
carrinho.append("cafe")
expect len(carrinho) is 1Se o segundo trial visse o carrinho do primeiro, a ordem dos testes passaria a importar — e um teste cuja aprovação depende da ordem não prova nada.
No CI#
dataforge check .
dataforge test tests/ --cobertura --minimo=80
dataforge lint src/
dataforge fmt . --checkOs quatro saem com código diferente de zero quando acham algo — é o que o CI lê. E --minimo é o que impede a cobertura de cair devagar até virar decoração.
O que testar, e o que não#
| Vale o teste | Não vale |
|---|---|
| a regra de negócio | o que a linguagem já garante |
a borda (vazio, zero, negativo, void) | que um + soma |
| o tipo do erro levantado | o texto exato da mensagem |
| o que já quebrou uma vez | código que só repassa valor |
| o contrato de um módulo público | a ordem interna das chamadas |
A linha mais importante é a quarta: todo bug corrigido merece um teste que falha sem a correção. É a única forma de ele não voltar — e é a regra que este repositório segue para as centenas de correções que carrega.