Pular para o conteúdo

A pirâmide de testes do NextPDF

Spec: ISO/IEC/IEEE 29119-4 Spec: ISO/IEC 25010 Evidence: Test-backed PHPStan: Level 10

O NextPDF não depende de um único tipo de teste. Ele tem cinco níveis, e cada um responde a uma pergunta diferente sobre o mecanismo. O motivo é simples: um PDF pode passar em um teste de unidade e ainda assim ser um arquivo estruturalmente quebrado em disco. Esta página apresenta os cinco níveis e o que cada um precisa comprovar.

Um mecanismo de PDF tem uma superfície de falhas excepcionalmente ampla. O mesmo caminho de código pode estar correto como função e correto como fluxo de bytes, e ainda assim produzir um arquivo que um leitor em conformidade rejeita. Ele também pode produzir um arquivo cuja renderização só fica sutilmente errada em uma quebra de página. Testar o mecanismo em uma única granularidade dá confiança exatamente nessa granularidade, e em nada mais.

A literatura normativa é clara sobre isso. As técnicas de projeto de teste baseadas em especificação e em estrutura não se relacionam entre si, e recomenda-se que uma estratégia de teste use mais de um critério, com pelo menos um funcional e um estrutural (ISO/IEC/IEEE 29119-4, Annex A). Um único nível não é uma versão reduzida de uma boa estratégia. É uma estratégia diferente, e incompleta.

O NextPDF organiza os testes em cinco níveis, da base ao ápice:

  1. Unidade — uma classe ou função, isoladamente. A base larga.
  2. Integração — unidades que colaboram através de uma fronteira de módulo.
  3. Estrutural — o grafo de objetos do PDF emitido, a tabela de referências cruzadas e o trailer estão bem formados e em conformidade.
  4. Visual — a página renderizada corresponde a uma referência aprovada dentro de uma tolerância declarada.
  5. Golden — fixtures de ponta a ponta fixas que capturam desvios não intencionais na saída final. O ápice.

Cada nível comprova algo que o nível abaixo dele não consegue. Nenhum deles é decorativo. O formato de pirâmide diz respeito a quantidade — muitos testes de unidade baratos, poucos testes de ponta a ponta caros — não a importância.

Os níveis são físicos, não aspiracionais. A configuração do PHPUnit no repositório declara cada um como uma suíte de testes nomeada, com correspondência um a um com um diretório. Um nível é, portanto, um lugar para o qual você pode apontar um executor de testes, não um rótulo em um slide. As suítes que um engenheiro sênior reconhecerá incluem Unit, Integration, Golden, Snapshot, Reproducibility, Conformance, Standards e Performance, cada uma com seu próprio perfil de execução (isolamento, orçamento de tempo e se é executada por padrão na integração contínua).

Essa separação é deliberada. O nível-base rápido (Unit) é executado a cada alteração com um orçamento de um segundo por teste. Os níveis mais lentos e sensíveis ao ambiente — renderização visual, conformidade completa, desempenho — são opcionais ou noturnos. Isso mantém o caminho comum rápido e determinístico sem abrir mão das verificações mais profundas. A tipagem estrita sustenta toda a pilha. O mecanismo executa a análise em Spec: PHPStan, Level 10 com o orçamento de erros travado em zero, de modo que uma grande classe de defeitos nem chega a um teste.

  1. Tier 1 of 5 Unit Isolated behaviour of a single class or function; the broad base.
  2. Tier 2 of 5 Integration Collaborating units across a module boundary.
  3. Tier 3 of 5 Structural The emitted PDF object/xref structure is well-formed and conformant.
  4. Tier 4 of 5 Visual Rendered output matches an approved reference within tolerance.
  5. Tier 5 of 5 Golden End-to-end byte/lossless fixtures pinned as the contract; the apex.
Os cinco níveis de teste do NextPDF, da base ao ápice. Unidade é a base larga e rápida; cada nível acima comprova uma propriedade que o nível abaixo não consegue, até as fixtures golden de ponta a ponta no ápice. A largura é apenas uma indicação de quantidade — ela não classifica importância.

Evidence: Test-backed As cinco suítes existem como suítes de testes PHPUnit declaradas na configuração do mecanismo, cada uma vinculada a seu próprio diretório e perfil de execução. O vocabulário de níveis nesta página é o mesmo usado pela infraestrutura de testes.

Evidence: Standard-backed A razão para mais de um nível está ancorada em Spec: ISO/IEC/IEEE 29119-4, Annex A : nem todos os critérios de cobertura se relacionam entre si, e recomenda-se que uma estratégia combine técnicas funcionais e estruturais. Crucialmente, o mesmo anexo observa que a ordenação por subsunção entre critérios de cobertura não dá nenhuma indicação de sua capacidade de expor defeitos — eficácia dos testes (ISO/IEC/IEEE 29119-4, §C.2.4). “Mais cobertura” não é a mesma afirmação que “testes melhores”.

Evidence: Standard-backed A escolha de quais propriedades comprovar corresponde às características de qualidade de produto da Spec: ISO/IEC 25010 : correção funcional (unidade, integração) e as propriedades no nível do arquivo que tornam um PDF realmente utilizável a jusante (estrutural, visual, golden). O modelo de qualidade é explícito ao afirmar que características diferentes têm importância em diferentes contextos de uso.

Os níveis são acessíveis a partir dos próprios scripts do mecanismo. Uma alteração em um único formatador é verificada na base. Uma alteração na fachada do documento é verificada em todos os níveis:

<?php
declare(strict_types=1);
// Tier 1 — Unit: one unit, isolated, fast.
// composer test:unit → phpunit --testsuite Unit
// Tier 2 — Integration: collaborating units across a boundary.
// composer test:integration → phpunit --testsuite Integration
// Tier 3 — Structural: the emitted PDF object graph is well-formed.
// vendor/bin/phpunit --testsuite Conformance
// Tier 4/5 — Visual + Golden: rendered/serialized output vs a pinned
// reference (golden is byte/structure-pinned, never auto-updated).
// vendor/bin/phpunit --testsuite Golden
// A change to the document facade touches every API, so the routing
// guidance escalates it from "unit only" to the full unit + integration
// surface — the tier you run is a function of blast radius, not habit.

O ponto do exemplo é a lógica de roteamento, não os comandos. O nível que você exercita é escolhido pelo que a alteração pode quebrar. A infraestrutura transforma cada nível em um alvo de primeira classe, executável separadamente.

A pirâmide costuma ser lida como uma classificação — testes de unidade na base porque importam menos, testes de ponta a ponta no topo porque importam mais (ou o contrário). Não é nem uma coisa nem outra. O eixo vertical corresponde, grosso modo, a custo e quantidade: muitos testes de unidade rápidos e baratos formando uma base larga; progressivamente menos testes, mais lentos e de maior fidelidade, acima. Um teste golden não é “melhor” que um teste de unidade. Ele captura uma falha diferente, mais tarde e com custo maior, e seria um substituto ruim para os milhares de verificações rápidas abaixo dele.

O segundo equívoco é achar que um número alto de cobertura significa que a pirâmide é sólida. Não significa. A cobertura mede execução, não detecção. As normas rejeitam explicitamente equiparar a ordenação de cobertura à capacidade de encontrar defeitos. Essa lacuna é exatamente o que o teste de mutação existe para expor.

Esta página descreve o formato e a intenção da estratégia, não os resultados atuais dela. Contagens de testes, porcentagens de cobertura e pontuações de mutação estão deliberadamente ausentes aqui. São sinais de qualidade dinâmicos, gerados a partir de artefatos de integração contínua. Os números atuais são publicados junto com o build. Se fossem congelados em prosa, ficariam silenciosamente desatualizados. O único número declarado — PHPStan Level 10 — é um fato de configuração estável, verificável na configuração de análise estática do mecanismo, não uma medição.

Os nomes dos níveis fazem parte de um vocabulário de arquitetura estável. O conjunto preciso de suítes e seus perfis de execução evolui com o mecanismo e é responsabilidade da configuração de testes, que é a autoridade se algum dia divergir desta explicação. Esta página não afirma nenhuma taxa de aprovação específica e não faz comparação com a estratégia de testes de nenhuma outra biblioteca.

  • Nível de teste — um nível da estratégia que comprova um tipo de propriedade (por exemplo, comportamento de unidade ou validade estrutural). O NextPDF usa cinco.
  • Teste estrutural — uma verificação de que o grafo de objetos do PDF emitido, a tabela de referências cruzadas e o trailer são bem formados e estão em conformidade, em oposição a meramente verificar um valor de retorno.
  • Teste visual — uma verificação de que uma página renderizada corresponde a uma imagem de referência aprovada dentro de uma tolerância declarada.
  • Teste golden — uma verificação de ponta a ponta contra uma saída de referência fixada que nunca é atualizada automaticamente; o contrato de que “a saída não mudou”.
  • Eficácia dos testes — a capacidade de um conjunto de testes de expor defeitos, que a ISO/IEC/IEEE 29119-4 distingue da cobertura. Nota sobre o acrônimo: MSI (Mutation Score Indicator) é definido na página de teste de mutação.
  • PHPStan Level 10 — o nível mais estrito de análise estática; o NextPDF o executa com o orçamento de erros travado em zero.