Ga naar inhoud

De testpiramide van NextPDF

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

NextPDF gebruikt niet één soort test. Het werkt met vijf lagen, en elke laag beantwoordt een andere vraag over de engine. Dat is nodig omdat een PDF een unittest kan doorstaan en toch als bestand op schijf structureel onjuist kan zijn. Deze pagina beschrijft de vijf lagen en wat elke laag moet aantonen.

Een PDF-engine heeft een ongewoon groot faaloppervlak. Hetzelfde codepad kan functioneel correct zijn en als bytesstroom kloppen, maar toch een bestand opleveren dat een conforme reader afwijst. Het kan ook een bestand opleveren dat alleen rond een pagina-einde subtiel verkeerd wordt weergegeven. Test je de engine op maar één granulariteitsniveau, dan krijg je vertrouwen in precies dat niveau en niets anders.

De relevante normen zijn hier duidelijk over. Specificatiegebaseerde en structuurgebaseerde testontwerptechnieken vallen niet met elkaar samen, en voor een teststrategie wordt aangeraden meer dan één criterium te gebruiken, met ten minste één functioneel en één structureel criterium (ISO/IEC/IEEE 29119-4, Annex A). Eén enkele laag is geen kleinere versie van een goede strategie. Het is een andere strategie, en een onvolledige.

NextPDF ordent zijn tests in vijf lagen, van basis tot top:

  1. Unit — één class of functie, in isolatie. De brede basis.
  2. Integratie — samenwerkende units over een modulegrens heen.
  3. Structureel — de gegenereerde PDF-objectgraaf, cross-reference-tabel en trailer zijn welgevormd en conform.
  4. Visueel — de weergegeven pagina komt binnen een opgegeven tolerantie overeen met een goedgekeurde referentie.
  5. Golden — vastgepinde end-to-end-fixtures die onbedoelde drift in de uiteindelijke uitvoer opvangen. De top.

Elke laag toont iets aan dat de laag eronder niet kan aantonen. Geen enkele laag is decoratie. De piramidevorm gaat over hoeveelheid — veel goedkope unittests, minder dure end-to-end-tests — niet over belang.

De lagen zijn concreet, niet aspirationeel. De PHPUnit-configuratie van de repository definieert elke laag als een benoemde testsuite die één-op-één aan een directory is gekoppeld. Een laag is daardoor een plek waarop je een runner kunt richten, niet alleen een label op een dia. Tot de suites die een ervaren engineer zal herkennen, behoren Unit, Integration, Golden, Snapshot, Reproducibility, Conformance, Standards en Performance, elk met een eigen uitvoeringsprofiel (isolatie, tijdsbucket, en of de suite standaard draait in continuous integration).

Die scheiding is bewust aangebracht. De snelle basislaag (Unit) draait bij elke wijziging met een budget van één seconde per test. De tragere, omgevingsgevoelige lagen — visuele weergave, volledige conformiteit, prestaties — zijn opt-in of nachtelijk. Zo blijft het standaardpad snel en deterministisch zonder de diepere controles op te geven. Strikte typering ligt aan de basis van de hele stack. De engine draait analyse op Spec: PHPStan, Level 10 met het foutbudget vergrendeld op nul, zodat een grote klasse defecten nooit een test bereikt.

  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.
De vijf testlagen van NextPDF, van basis tot top. Unit is de brede, snelle basis; elke laag erboven toont een eigenschap aan die de laag eronder niet kan aantonen, tot aan golden end-to-end-fixtures op de top. Breedte is uitsluitend een hint over hoeveelheid — het rangschikt geen belang.

Evidence: Test-backed De vijf suites zijn gedeclareerd als PHPUnit-testsuites in de configuratie van de engine, elk gekoppeld aan een eigen directory en uitvoeringsprofiel. De terminologie voor de lagen op deze pagina is dezelfde als die van de testinfrastructuur.

Evidence: Standard-backed De reden voor meer dan één laag is verankerd in Spec: ISO/IEC/IEEE 29119-4, Annex A : niet alle dekkingscriteria vallen met elkaar samen, en voor een strategie wordt aangeraden functionele en structurele technieken te combineren. Cruciaal is dat dezelfde bijlage opmerkt dat de subsumes-ordening tussen dekkingscriteria niets zegt over hun vermogen om fouten bloot te leggen — testeffectiviteit (ISO/IEC/IEEE 29119-4, §C.2.4). „Meer dekking” is niet dezelfde bewering als „betere tests”.

Evidence: Standard-backed De keuze van welke eigenschappen je aantoont, sluit aan op de productkwaliteitskenmerken van Spec: ISO/IEC 25010 : functionele correctheid (unit, integratie), en de eigenschappen op bestandsniveau die een PDF stroomafwaarts daadwerkelijk bruikbaar maken (structureel, visueel, golden). Het kwaliteitsmodel maakt expliciet dat verschillende kenmerken in verschillende gebruikscontexten van belang zijn.

De lagen zijn aanspreekbaar via de eigen scripts van de engine. Een wijziging aan één enkele formatter wordt op de basislaag geverifieerd. Een wijziging aan de document-facade wordt over meerdere lagen heen geverifieerd:

<?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.

Het punt van het voorbeeld is de routeringslogica, niet de commando’s. Welke laag je draait, wordt bepaald door wat de wijziging kan breken. De infrastructuur maakt van elke laag een volwaardig, afzonderlijk uitvoerbaar doel.

De piramide wordt vaak gelezen als een rangschikking — unittests onderaan omdat ze het minst belangrijk zijn, end-to-end bovenaan omdat ze het belangrijkst zijn (of andersom). Het is geen van beide. De verticale as gaat grofweg over kosten en hoeveelheid: veel snelle, goedkope unittests die een brede basis vormen; daarboven steeds minder, tragere tests met hogere getrouwheid. Een golden-test is niet „beter” dan een unittest. Die vangt een andere fout op, later, tegen hogere kosten, en zou een slechte vervanging zijn voor de duizenden snelle controles eronder.

Het tweede misverstand is dat een hoog dekkingsgetal betekent dat de piramide deugdelijk is. Dat betekent het niet. Dekking meet uitvoering, niet detectie. De normen stellen de ordening van dekking expliciet niet gelijk aan het vermogen om fouten te vinden. Precies dat gat legt mutatietesten bloot.

Deze pagina beschrijft de vorm en bedoeling van de strategie, niet de huidige resultaten. Aantallen tests, dekkingspercentages en mutatiescores ontbreken hier bewust. Het zijn levende kwaliteitssignalen, gegenereerd uit continuous-integration-artefacten. De huidige cijfers worden bij de build gepubliceerd. In proza vastgelegd zouden ze stilletjes verouderen. Het enige genoemde getal — PHPStan Level 10 — is een stabiel configuratiefeit, verifieerbaar in de configuratie voor statische analyse van de engine, en geen meting.

De namen van de lagen vormen stabiele architectuurterminologie. De precieze verzameling suites en hun uitvoeringsprofielen evolueert mee met de engine en valt onder de testconfiguratie; die is leidend als zij ooit afwijkt van deze uitleg. Deze pagina claimt geen specifiek slaagpercentage en maakt geen vergelijking met de teststrategie van een andere bibliotheek.

  • Testlaag — een niveau van de strategie dat één soort eigenschap aantoont (bijvoorbeeld unitgedrag of structurele geldigheid). NextPDF gebruikt er vijf.
  • Structurele test — een controle of de objectgraaf, cross-reference-tabel en trailer van de gegenereerde PDF welgevormd en conform zijn, in plaats van alleen een retourwaarde te controleren.
  • Visuele test — een controle dat een weergegeven pagina binnen een opgegeven tolerantie overeenkomt met een goedgekeurde referentieafbeelding.
  • Golden-test — een end-to-end-controle tegen een vastgepinde referentie-uitvoer die nooit automatisch wordt bijgewerkt; het contract voor „de uitvoer is niet veranderd”.
  • Testeffectiviteit — het vermogen van een testset om fouten bloot te leggen, dat ISO/IEC/IEEE 29119-4 onderscheidt van dekking. Opmerking over acroniem: MSI (Mutation Score Indicator) wordt gedefinieerd op de pagina mutatietesten.
  • PHPStan Level 10 — het strengste niveau van statische analyse; NextPDF draait het met het foutbudget vergrendeld op nul.