27Testing+, zplcore check & GitHub Action
Etiketten sind Code – testen Sie sie wie Code. ZplCore.Testing prüft, was ein Etikett druckt, nicht nur wie es aussieht: jeder Barcode wird mit zwei Decodern zurückgelesen, jedes Element vermessen. Schlägt etwas fehl, nennt die Meldung Element, Position (Dots, mm, ZPL-Zeile) und ein Bild mit markiertem Element. Läuft mit xUnit, NUnit, MSTest und jedem anderen Runner.
dotnet add reference src/ZplCore.Testing/ZplCore.Testing.csproj
// using ZplCore.Compliance; using ZplCore.Testing;
ZplAssert.Label(zpl)
.HasLabels(1)
.NoOverlaps()
.NoClipping()
.Complies(CompliancePacks.Gs1LogisticLabel)
.Barcode("SSCC").Decodes("(00)376104250021234569").HasGrade(atLeast: 'B').HasQuietZones()
.And.Text("RETAIL DC").IsWithinLabel().DoesNotOverlap("SSCC")
.And.FitsPrinter(ZplCore.Twin.PrinterProfiles.Get("ZD421d-203"))
.MatchesSnapshot("pallet"); // Pixel + Semantik, Freigabe mit ZPLCORE_ACCEPT_SNAPSHOTS=1
Fehlermeldung mit Bild

Was geprüft werden kann
HasLabels,Renders,NoOverlaps(echte Druckfarbe, Rahmen zählen nicht),NoClippingComplies(CompliancePacks…),AllBarcodesDecode(),AllBarcodesHaveGrade('C')Barcode("SSCC"):Decodes,HasGrade,HasQuietZones,HasXDimension,IsGs1Text("…"):IsWithinLabel,DoesNotOverlap,IsAbove/IsBelow,HasCharHeightFitsPrinter(PrinterProfile)mit dem Printer Twin,FieldEquals("FN1", …)MatchesSnapshot: Pixel und Semantik – ein fehlschlagender Snapshot sagt, was sich geändert hat („Text verschoben um +12 Dots“)
Label(zpl).Barcode("SSCC").Decodes("(00)376104250021234576") failed: the decoders read "(00)376104250021234569", expected "(00)376104250021234576".
element ^BC Code128 "(00)376104250021234569" at x=94 y=800 (11.8 / 100.1 mm), 624 × 260 dots (78.1 × 32.5 mm) · line 17, col 1
source ^FO94,800^BCN,260,Y,N,N,D^FD(00)376104250021234569^FS
decoders zxing-cpp: "(00)376104250021234569" · ZXing.Net: "(00)376104250021234569"
CI ohne Testprojekt: zplcore check
Dieselben Prüfungen über viele Dateien: Rendern, Layout, Compliance-Pakete, Scan-Proof, Druckerprofil, Snapshots und Vergleich mit dem Basis-Branch – Ausgabe als Text, JUnit-XML, SARIF, JSON, Markdown (für PR-Kommentare) oder GitHub-Annotationen. Exit-Codes 0/1/2.
zplcore check "labels/**/*.zpl" --pack gs1-logistic --verify --min-grade C --layout --printer ZD421d-203 zplcore check labels/ --snapshots tests/__snapshots__ --format junit -o labels.junit.xml zplcore check labels/ --baseline base/ --report-dir report/ --format markdown -o report.md # vorher/nachher/diff-PNGs zplcore snapshot accept tests/__snapshots__
GitHub Action
Die Composite Action tools/github-action prüft in jedem Pull Request die geänderten Etikettendateien gegen den Basis-Branch und schreibt Annotationen, eine Job-Zusammenfassung, einen aktualisierten PR-Kommentar mit Vorher-/Nachher-/Diff-Bildern und SARIF für Code Scanning. Sie liegt im Lieferumfang; die Veröffentlichung im Marketplace als zplcore/label-check ist vorgesehen. Bis dahin: Ordner ins eigene Repository kopieren und die CLI aus Ihrem Paket-Feed installieren lassen.
name: Label check
on:
pull_request:
paths: ["**/*.zpl", "**/*.json"]
permissions:
contents: read
pull-requests: write # PR-Kommentar
security-events: write # SARIF → Code Scanning
jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/label-check # Kopie von tools/github-action
with:
packs: gs1-logistic
min-grade: C
nuget-source: https://nuget.example.com/v3/index.json # Feed mit dem Paket ZplCore.Cli
Die Note ist eine digitale Vorprüfung nach dem Vorbild von ISO/IEC 15415/15416, keine ISO-Verifikation; Compliance-Pakete sind Pre-Checks, keine Zertifizierungen. Fork-PRs ohne Schreibrechte bekommen statt Kommentar und SARIF nur Annotationen und die Job-Zusammenfassung.
Gates in der CI und im Container-Build
Eine fehlerhafte Vorlage, die in Produktion ankommt, wird teuer: Barcodes, die an der Rampe nicht scannen, stehende Linien, Sendungen, die der Versanddienstleister ablehnt. Dieselben Prüfungen laufen deshalb überall, wo sich eine Vorlage ändert – im Commit, im Pull Request, in der Pipeline und im Container-Build – und entscheiden deterministisch: gleiche Vorlage, gleiches Ergebnis, Exit-Code 0 oder 1.
CI-Gate (jede Pipeline)
# Exit-Code 1 bei Fehlern, Berichte für die Pipeline zplcore check templates/ --pack gs1-logistic \ --verify --min-grade B --printer ZD421d-203 \ --junit out/labels.junit.xml --sarif out/labels.sarif
Auch in Azure DevOps, GitLab CI oder Jenkins: CLI, Exit-Codes und JUnit-XML.
Gate im Container-Build
# Build-Stufe: Vorlagen prüfen, bevor ein Image entsteht
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS label-gate
WORKDIR /src
COPY nuget.config ./
COPY .config/ .config/
COPY templates/ templates/
RUN dotnet tool restore \
&& dotnet zplcore check templates/ --pack gs1-logistic \
--verify --min-grade B --printer ZD421d-203
FROM mcr.microsoft.com/dotnet/aspnet:10.0
# nur geprüfte Vorlagen landen im Image
COPY --from=label-gate /src/templates /app/templatesSo sieht ein gestopptes Gate aus (echte Ausgabe, gekürzt):
PASS templates/dhl-100x150.zpl
FAIL templates/express-100x150.zpl
FAIL comply gs1-logistic
:38:1 error ZPL3104 X-dimension 0.375 mm < 0.495 mm
:38:1 error ZPL3105 bar height 13.0 mm < 31.75 mm
FAIL scan-proof
:32:1 error ZT1301 QR Code grade D, required B
ok printer ZD421d (ZD421d-203)
FAILED (fail-on error) · exit 1
Schnitte und RFID im Gate. Eine kleine Änderung an ^MM oder ^PQ schneidet plötzlich jedes Label oder keines; ein festes ^RF schreibt dieselbe EPC auf jede Kopie. Beides fällt meist erst an der Linie auf – im Gate ist es vorher geklärt (Regel ZPL3909: dieselbe EPC auf mehreren Labels; ZPL3904: EPC passt nicht zum Barcode).
# CI: RFID-Paket im Gate, EPC je Label anzeigen zplcore check templates/ --pack gs1-logistic,rfid zplcore rfid simulate templates/palette.zpl # Staging: Drucker mit Cutter und RFID simulieren zplcore serve --profile ZT411-203 --option cutter,rfid --rfid --cut-marks
// Schnitte: wo der Cutter schneidet
var plan = ZplCutPlan.Analyze(zpl);
Assert.Equal([2, 5], plan.CutAfter); // nach Label 3 und 6
// RFID: EPC passt zum Barcode, keine doppelte EPC
ZplAssert.Label(zpl)
.Complies(CompliancePacks.Rfid)
.FitsPrinter(PrinterProfiles.Get("ZT411-203").WithOptions(PrinterOption.Cutter | PrinterOption.Rfid).AsPrinterFit());
Von KI erklärt, von Menschen freigegeben. Ein Coding-Agent (Claude Code, GitHub Copilot, Cursor) prüft Änderungen über zplcore-mcp (comply, verify, printer_check, render, diff), erklärt die Befunde und schlägt die Korrektur als Commit vor. Die Freigabe bleibt bei Ihnen, z. B. mit Pflicht-Reviewern für Label-Änderungen über CODEOWNERS und Branch-Schutz.

