Free · MIT · Composer dev package for Pest and PHPUnit
Your PDF fake passed. Is the invoice total there?
Check the PDF your Laravel app generates. Assert that the invoice contains the right total, or compare its layout with an approved snapshot. A fake checks that you requested a PDF. These assertions check the resulting document.
Pass a Laravel test response, a file path or raw PDF bytes. Spatie Laravel PDF and Dompdf objects work too. No browser or hosted service required.
What happens when the total goes missing
These three Pest tests check an invoice route. In this example, the total is missing: the text check shows what was extracted, and the snapshot check points to the diff. The figures shown are illustrative.
tests/Feature/InvoicePdfTest.php
it('has one page', function () {
expect($this->get('/invoices/1001/pdf'))
->toHavePdfPageCount(1);
});
it('shows the total', function () {
expect($this->get('/invoices/1001/pdf'))
->toHavePdfText('Invoice 1001')
->toHavePdfText('120.00');
});
it('matches the approved layout', function () {
expect($this->get('/invoices/1001/pdf'))
->toMatchPdfSnapshot('invoice-1001');
});vendor/bin/pest
FAIL Tests\Feature\InvoicePdfTest
✓ has one page
⨯ shows the total
⨯ matches the approved layout
⨯ shows the total
Expected PDF to contain "120.00".
Extracted text:
Invoice 1001 Acme Ltd
Design work 100.00
VAT 20.00
Total
⨯ matches the approved layout
PDF snapshot page 1: 312 changed pixels (allowed 0).
Diff: tests/pdf-snapshots/invoice-1001/artifacts/diff-1.png
Tests: 2 failed, 1 passedThree checks on the real document
- toHavePdfText()
- Checks for a case-sensitive substring in the PDF's text layer. It does not use OCR or check layout.
- On failure
Expected PDF to contain "120.00". Extracted text: … - toHavePdfPageCount()
- Checks the page count, so an unexpected second page fails the test.
- On failure
Expected PDF to have 1 page(s); found 2. - toMatchPdfSnapshot()
- Renders each page and compares it with an approved baseline. You can mask one area or allow a set number of changed pixels per page.
- On failure
PDF snapshot page 1: 312 changed pixels (allowed 0). Diff: …/diff-1.png
PHPUnit users can call the same checks as static PdfAssertions methods.
Install
Add it to an existing Laravel 12 app with Pest or PHPUnit. The second command installs the pinned renderer inside the package. Installation needs network access. Assertions run offline.
$ composer require --dev 'craigsimps/assertpdf:^0.1' -W
$ vendor/bin/assertpdf installExisting Pest installations need no registration step. Use your own PDF generator. Full details in the README.
You need
- PHP 8.3 or 8.4
- Laravel 12 (optional; plain PHPUnit projects work too)
- Pest 3 or PHPUnit 11.5; Pest 4 or PHPUnit 12 (tested on PHP 8.4)
- Node 24.21.0 and npm 11, used only by the renderer
- No browser, Poppler, Ghostscript or hosted service required
Offline after install. Assertions use in-memory PDF data, bundled fonts and a local Node process. No network requests, no telemetry.
A complete example
A small Laravel app with an invoice PDF route and passing PDF tests. You can clone it and change the invoice to see which tests fail.
You control snapshot updates
A missing baseline fails. Nothing is created or overwritten unless you ask.
- 01
Create the baseline explicitly.
ASSERTPDF_UPDATE_SNAPSHOTS=1 vendor/bin/pest --filter='approved layout' - 02
Review the PNGs in
tests/pdf-snapshots/invoice-1001/before you accept them. On a mismatch,artifacts/diff-1.pngshows the changed pixels in red. - 03
Commit
manifest.jsonandpage-*.png, ignoreartifacts/, and never set the update flag in normal CI.Create and compare baselines on the same pinned renderer, operating system and architecture. Snapshots are not portable across operating systems.
Limits
Use this for PDFs your app generates. These limits apply to every assertion.
- Not OCR. Scanned PDFs without a text layer, and password-protected PDFs, are out of scope.
- Not a replacement for fakes. Keep testing that your app requested a PDF. This tests what the PDF contains.
- Not a service. Everything runs on your machine or CI. The toolkit collects no telemetry.
- Not for untrusted uploads. It is for your own application and test PDFs, with a 60-second timeout per assertion.