Przejdź do głównej zawartości

Piramida testów w NextPDF

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

NextPDF nie opiera się na jednym rodzaju testów. Ma pięć poziomów, a każdy z nich odpowiada na inne pytanie dotyczące silnika. Powód jest prosty: plik PDF może przejść test jednostkowy, a mimo to nadal być strukturalnie uszkodzonym plikiem na dysku. Ta strona wymienia pięć poziomów i wskazuje, czego dowodzi każdy z nich.

Silnik PDF ma niezwykle szeroki obszar potencjalnych awarii. Ta sama ścieżka kodu może być poprawna jako funkcja i jako strumień bajtów, a mimo to wytworzyć plik, który odrzuci czytnik zgodny ze standardem. Może też wytworzyć plik, który renderuje się subtelnie błędnie dopiero na granicy podziału strony. Przetestowanie silnika na jednym poziomie szczegółowości daje pewność tylko na tym poziomie i co do niczego więcej.

Literatura normalizacyjna mówi o tym jasno. Techniki projektowania testów oparte na specyfikacji oraz oparte na strukturze nie są ze sobą powiązane, a w strategii testów zaleca się stosowanie więcej niż jednego kryterium, w tym co najmniej jednego funkcjonalnego i jednego strukturalnego (ISO/IEC/IEEE 29119-4, Annex A). Strategia oparta na jednym poziomie nie jest mniejszą wersją dobrej strategii. To inna strategia, w dodatku niekompletna.

NextPDF porządkuje swoje testy w pięć poziomów, od podstawy po wierzchołek:

  1. Jednostkowe — jedna klasa lub funkcja, w izolacji. Szeroka podstawa.
  2. Integracyjne — współpracujące jednostki na granicy modułu.
  3. Strukturalne — wyemitowany graf obiektów PDF, tablica odniesień krzyżowych i przyczepka są poprawnie sformułowane i zgodne ze standardem.
  4. Wizualne — wyrenderowana strona zgadza się z zatwierdzonym wzorcem w granicach podanej tolerancji.
  5. Wzorcowe — przypięte scenariusze testowe typu end-to-end, które wychwytują niezamierzony dryf końcowego wyniku. Wierzchołek.

Każdy poziom dowodzi czegoś, czego nie potrafi udowodnić poziom znajdujący się poniżej. Żaden z nich nie jest ozdobnikiem. Kształt piramidy dotyczy ilości — wielu tanich testów jednostkowych, mniejszej liczby kosztownych testów end-to-end — a nie hierarchii ważności.

Te poziomy są konkretne, a nie wyłącznie deklaratywne. Konfiguracja PHPUnit w repozytorium deklaruje każdy z nich jako nazwany zestaw testów odwzorowany jeden do jednego na katalog. Poziom jest zatem celem, do którego można skierować program uruchamiający testy, a nie etykietą na slajdzie. Zestawy, które rozpozna doświadczony inżynier, obejmują Unit, Integration, Golden, Snapshot, Reproducibility, Conformance, Standards oraz Performance; każdy z nich ma własny profil wykonania (izolację, budżet czasu oraz informację, czy uruchamia się domyślnie w ramach ciągłej integracji).

Ten podział jest zamierzony. Szybki poziom podstawowy (Unit) uruchamia się przy każdej zmianie z budżetem jednej sekundy na test. Wolniejsze, wrażliwe na środowisko poziomy — renderowanie wizualne, pełna zgodność, wydajność — włącza się opcjonalnie albo uruchamia nocą. Dzięki temu typowa ścieżka pozostaje szybka i deterministyczna bez rezygnacji z głębszych kontroli. Ścisłe typowanie stanowi fundament całego stosu. Silnik uruchamia analizę na poziomie Spec: PHPStan, Level 10 z budżetem błędów zablokowanym na zerze, więc duża klasa defektów w ogóle nie dociera do testu.

  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.
Pięć poziomów testów NextPDF, od podstawy po wierzchołek. Poziom jednostkowy to szeroka, szybka podstawa; każdy poziom powyżej dowodzi właściwości, której nie potrafi udowodnić poziom poniżej, aż po wzorcowe scenariusze end-to-end na wierzchołku. Szerokość jest jedynie wskazówką co do ilości — nie określa ważności.

Evidence: Test-backed Pięć zestawów jest zadeklarowanych jako zestawy testów PHPUnit w konfiguracji silnika; każdy jest powiązany z własnym katalogiem i profilem wykonania. Terminologia poziomów użyta na tej stronie jest tą samą terminologią, której używa infrastruktura testowa.

Evidence: Standard-backed Uzasadnienie potrzeby więcej niż jednego poziomu jest zakotwiczony w Spec: ISO/IEC/IEEE 29119-4, Annex A : nie wszystkie kryteria pokrycia są ze sobą powiązane, a strategii zaleca się łączenie technik funkcjonalnych i strukturalnych. Co istotne, ten sam załącznik zauważa, że relacja zawierania porządkująca kryteria pokrycia nie daje żadnych wskazówek dotyczących ich zdolności do ujawniania usterek — skuteczności testów (ISO/IEC/IEEE 29119-4, §C.2.4). Stwierdzenie „większe pokrycie” nie jest równoznaczne ze stwierdzeniem „lepsze testy”.

Evidence: Standard-backed Wybór tego, które właściwości udowodnić, odwzorowuje się na charakterystyki jakości opisane w Spec: ISO/IEC 25010 produktu: poprawność funkcjonalną (jednostkowe, integracyjne) oraz właściwości na poziomie pliku, które sprawiają, że plik PDF jest faktycznie użyteczny w dalszym przetwarzaniu (strukturalne, wizualne, wzorcowe). Model jakości wprost stwierdza, że różne charakterystyki mają znaczenie w różnych kontekstach użycia.

Poziomy są dostępne przez własne skrypty silnika. Zmiana w pojedynczym formaterze jest weryfikowana na poziomie podstawy. Zmiana w fasadzie dokumentu jest weryfikowana na wielu poziomach:

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

Sednem przykładu jest logika kierowania, a nie same polecenia. Wybór uruchamianego poziomu zależy od tego, co zmiana może zepsuć. Infrastruktura sprawia, że każdy poziom jest pełnoprawnym, osobno uruchamialnym celem.

Piramidę często odczytuje się jako ranking — testy jednostkowe na dole, ponieważ mają najmniejsze znaczenie, a testy end-to-end na górze, ponieważ mają największe znaczenie (lub odwrotnie). To nie jest ani jedna, ani druga interpretacja. Oś pionowa opisuje w przybliżeniu koszt i liczbę: wiele szybkich, tanich testów jednostkowych tworzących szeroką podstawę; stopniowo coraz mniej testów wolniejszych i o wyższej wierności powyżej. Test wzorcowy nie jest „lepszy” od testu jednostkowego. Wychwytuje inny rodzaj awarii, później i drożej, i byłby kiepskim zamiennikiem tysięcy szybkich kontroli znajdujących się poniżej.

Drugie nieporozumienie polega na tym, że wysoki wskaźnik pokrycia oznacza solidną piramidę. Tak nie jest. Pokrycie mierzy wykonanie, a nie wykrywanie. Normy wprost nie pozwalają utożsamiać uporządkowania kryteriów pokrycia ze zdolnością do wykrywania usterek. Właśnie tę lukę ma ujawniać testowanie mutacyjne.

Ta strona opisuje kształt i zamysł strategii, a nie jej bieżące wyniki. Liczby testów, procenty pokrycia oraz wyniki mutacji są tutaj celowo pominięte. Są to żywe sygnały jakości, generowane z artefaktów ciągłej integracji. Bieżące wartości są publikowane wraz z kompilacją. Gdyby zamrozić je w tekście, po cichu stałyby się nieaktualne. Jedyna podana liczba — PHPStan Level 10 — to stabilny fakt konfiguracyjny, możliwy do zweryfikowania w konfiguracji analizy statycznej silnika, a nie pomiar.

Same nazwy poziomów to stabilne słownictwo architektoniczne. Dokładny zbiór zestawów testowych i ich profile wykonania ewoluują wraz z silnikiem i należą do konfiguracji testów, która jest źródłem rozstrzygającym, jeśli kiedykolwiek będzie niezgodna z tym wyjaśnieniem. Ta strona nie podaje żadnego konkretnego wskaźnika zdawalności i nie porównuje tej strategii ze strategią testów żadnej innej biblioteki.

  • Poziom testów — warstwa strategii, która dowodzi jednego rodzaju właściwości (na przykład zachowania jednostki lub poprawności strukturalnej). NextPDF używa pięciu.
  • Test strukturalny — sprawdzenie, że graf obiektów wyemitowanego pliku PDF, tablica odniesień krzyżowych i przyczepka są poprawnie sformułowane i zgodne ze standardem, w przeciwieństwie do samego sprawdzenia wartości zwracanej.
  • Test wizualny — sprawdzenie, że wyrenderowana strona zgadza się z zatwierdzonym obrazem referencyjnym w granicach zadeklarowanej tolerancji.
  • Test wzorcowy — sprawdzenie typu end-to-end względem przypiętego wyniku referencyjnego, który nigdy nie jest aktualizowany automatycznie; kontrakt mówiący, że „wynik się nie zmienił”.
  • Skuteczność testów — zdolność zbioru testów do ujawniania usterek, którą ISO/IEC/IEEE 29119-4 odróżnia od pokrycia. Uwaga dotycząca akronimu: MSI (Mutation Score Indicator) jest zdefiniowany na stronie testowanie mutacyjne.
  • PHPStan Level 10 — najsurowszy poziom analizy statycznej; NextPDF uruchamia go z budżetem błędów zablokowanym na zerze.