Salta ai contenuti

Test con golden file

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

Un golden file è un riferimento registrato del tipo «ecco come si presenta l’output corretto», con cui un test si confronta a ogni esecuzione. NextPDF usa i golden per intercettare le modifiche non volute: un flusso compresso in modo diverso, un paragrafo spostato, una coordinata scostata. Questa pagina spiega come funziona il meccanismo e come un golden resta affidabile anziché diventare un riferimento obsoleto che nessuno legge.

La generazione di PDF è una pipeline lunga, con molti punti in cui può deviare silenziosamente. Un refactoring che «non cambia nulla» può riordinare di nascosto gli operatori, alterare una matrice di trasformazione o spostare una cella di tabella di una quantità minima. Gli unit test raramente lo intercettano: verificano un valore scelto esplicitamente, non le migliaia di byte lasciati fuori dal controllo. Le tecniche basate sulla struttura e quelle basate sulla specifica rilevano errori diversi e nessuna delle due include l’altra (ISO/IEC/IEEE 29119-4, Annex A). Un golden file è la specifica-per-esempio che blocca l’output nella sua interezza, non una singola asserzione.

Il rischio corre in entrambe le direzioni. Un golden troppo rigido fallisce a ogni modifica innocua e viene riapprovato alla cieca finché non dimostra più nulla. Un golden troppo permissivo lascia passare regressioni reali. La difficoltà sta tutta nel trovare il giusto equilibrio.

  • Un golden file è un output di riferimento bloccato, generato da un comportamento del motore noto come corretto e committato nel repository.
  • Un golden test rigenera l’output e lo confronta con il riferimento bloccato; qualsiasi differenza fa fallire il test e richiede una decisione umana.
  • NextPDF confronta al livello che è significativo ma stabile: testo estratto e operatori strutturali normalizzati, non i byte grezzi, perché i byte grezzi introducono rumore (marche temporali, ordinamento del subset, compressione) che non è una regressione.
  • Aggiornare un golden è un atto deliberato e revisionato, protetto da uno switch GOLDEN_UPDATE esplicito — mai un «accetta qualunque cosa sia cambiata» automatico.
  • Il golden si distingue dallo snapshot testing e dal characterization testing per un aspetto decisivo: un golden non viene mai aggiornato automaticamente.

L’infrastruttura golden del motore adotta un diff a due livelli esplicito anziché un confronto byte a byte:

  1. Generate Render the fixture input through the current engine.
  2. Layer 1 — text Extract human-readable text from the content stream; diff against the text golden. Catches dropped or reordered content and encoding regressions.
  3. Layer 2 — structure Extract ordered PDF operators, normalise coordinates to a fixed precision, diff against the operator golden. Catches layout shifts and broken structure.
  4. Decide Any diff fails the test; a human judges whether it is a regression or an intended change.
Come si svolge un confronto golden di NextPDF: si genera il PDF, si estraggono i livelli significativi (testo, poi operatori strutturali normalizzati), si confronta ciascuno con il riferimento bloccato e si fallisce con un report leggibile dall'uomo a ogni differenza.

Ciò che sceglie deliberatamente di non confrontare è tanto importante quanto ciò che confronta. L’output in byte grezzi è escluso perché marche temporali, ordinamento del subset dei font e compressione dei flussi lo rendono fragile senza renderlo più corretto. Il confronto di immagini a livello di pixel è escluso da questo livello perché richiede un renderer esterno e introduce la variabilità dell’ambiente. Le coordinate in virgola mobile sono normalizzate a una precisione fissa, in modo che il rumore di arrotondamento privo di significato non venga scambiato per una regressione. Questa è la differenza tra un golden che verifica il comportamento e uno che verifica il rumore ambientale transitorio.

Questa scelta definisce anche il profilo di riproducibilità della pagina: strutturale. NextPDF documenta tre profili — bitwise (si riproducono i byte esatti), strutturale (si riproducono il grafo degli oggetti e la sequenza degli operatori, ammettendo una variabilità benigna a livello di byte) e semantico (si riproduce il significato). Per costruzione, i golden test a questo livello affermano il profilo strutturale. Ecco perché i loro riferimenti sopravvivono a un aggiornamento della libreria di compressione, ma falliscono comunque su una tabella spostata.

Evidence: Test-backed Il confronto a due livelli (estrazione del testo, poi confronto degli operatori strutturali normalizzati) è la metodologia golden documentata dal motore stesso, con il confronto dei byte grezzi, image-diff e confronto esatto dei float esplicitamente fuori ambito per i motivi sopra esposti. La suite golden è una suite di test dichiarata ed eseguibile separatamente, distinta dalle suite snapshot e characterization.

Evidence: Test-backed Il meccanismo che mantiene onesti i golden è concreto: i riferimenti golden sono artefatti generati, non scritti a mano. Sovrascriverli è protetto da uno switch d’ambiente GOLDEN_UPDATE esplicito, documentato come operazione rara e sempre revisionata. Nel motore, invece, gli snapshot test si rigenerano alla prima esecuzione e riconoscono lo scostamento tramite un flag di aggiornamento; i characterization test bloccano il comportamento legacy senza affermare che sia corretto. I tre sono intenzionalmente strumenti diversi.

Evidence: Standard-backed Un golden è una specifica-per-esempio. Spec: ISO/IEC/IEEE 29119-4, Annex A indica che le tecniche basate sulla specifica e quelle basate sulla struttura intercettano classi diverse di errore e che una strategia dovrebbe combinarle. Ecco perché i golden si affiancano, e non sostituiscono, i test unitari e strutturali nella piramide dei test.

Un golden test è meccanicamente semplice; la disciplina sta nel flusso di lavoro che lo circonda:

<?php
declare(strict_types=1);
// 1. The fixture: a fixed HTML input committed next to the test.
// tests/Golden/fixtures/html-inputs/002-basic-table.html
// 2. The pinned references, generated once from known-good behaviour:
// 002-basic-table.text.golden (Layer 1 — extracted text)
// 002-basic-table.operators.golden (Layer 2 — normalised operators)
// 3. The run compares; ANY difference fails:
// vendor/bin/phpunit --testsuite Golden
// 4. An intended behaviour change is the ONLY time references move,
// and it is explicit and reviewed — never automatic:
// GOLDEN_UPDATE=1 vendor/bin/phpunit --testsuite Golden
//
// The regenerated *.golden files land in the diff of the same change
// that altered behaviour, so a reviewer sees the output delta next to
// the code delta and signs off on both together.

Qui l’esempio è il processo. Il codice del test si limita a confrontare. Ciò che rende affidabile il golden è che un riferimento modificato viene revisionato come output nella stessa modifica che ha alterato il motore.

L’errore più comune è trattare il golden testing come un test byte a byte. I golden di NextPDF non sono i byte del file, ma il suo testo estratto e i suoi operatori strutturali normalizzati. Verificare i byte grezzi fallirebbe su una nuova versione di zlib, un diverso tag di subset o una marca temporale rigenerata, e nessuna di queste differenze è una regressione. Nel giro di una settimana il test verrebbe riapprovato fino a renderlo inutile. (Quando è davvero necessario riprodurre i byte esatti, ciò rientra nel profilo di riproducibilità bitwise separato e più rigido, non in un golden.)

Il secondo errore è presumere che una suite golden verde dimostri la correttezza. Dimostra l’assenza di cambiamento. Un golden generato da un output difettoso protegge fedelmente il difetto. I golden proteggono dalle regressioni rispetto a una baseline nota come corretta; non stabiliscono che la baseline fosse corretta. A questo servono i livelli unitario, strutturale e di conformità.

Un golden test risponde a esattamente una domanda: l’output è cambiato rispetto al riferimento bloccato? Non dice se il riferimento sia mai stato corretto. Né misura prestazioni, conformità o sicurezza. Quelli sono altri livelli. La dimensione del corpus di fixture, il tasso di superamento della suite e qualsiasi dato di copertura sono indicatori di qualità vivi, generati dagli artefatti di integrazione continua e pubblicati con la build. Non sono riportati qui di proposito, dove potrebbero diventare obsoleti.

Il layout esatto delle directory, i dettagli interni del comparatore e lo switch di aggiornamento sono di competenza dell’infrastruttura di test del motore e possono evolvere. La configurazione dei test è l’autorità qualora dovesse mai discostarsi da questa spiegazione. Questa pagina non avanza alcuna affermazione sugli strumenti snapshot o golden di altre librerie.

  • Golden file — un output di riferimento bloccato, generato da un comportamento del motore noto come corretto e committato, con cui un test si confronta a ogni esecuzione. Mai aggiornato automaticamente.
  • Diff a due livelli — il confronto golden di NextPDF: testo estratto (Livello 1) più operatori strutturali normalizzati (Livello 2), anziché byte grezzi.
  • Snapshot test — una tecnica correlata ma distinta in cui il riferimento viene rigenerato alla prima esecuzione e lo scostamento è riconosciuto tramite un flag di aggiornamento.
  • Characterization test — un test che blocca il comportamento esistente senza affermare che sia corretto, di norma per rendere sicuro un refactoring.
  • Profilo di riproducibilità — il livello al quale l’output deve riprodursi: bitwise (byte esatti), strutturale (grafo degli oggetti e sequenza degli operatori, con variabilità benigna dei byte ammessa) o semantico (significato). I golden test qui affermano il profilo strutturale.
  • GOLDEN_UPDATE — lo switch d’ambiente esplicito che autorizza la sovrascrittura dei riferimenti golden; un’operazione rara e revisionata.