Ignore and scrub
A snapshot records everything, so a test fails on the smallest change. When part of a document changes on every run, or doesn't matter to a test, leave it out or replace it. Every option is an enum value or a method, so you never need to know PDF or DOCX internals.
Pass options in configure; the calls chain:
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
.Ignore(PdfSection.Metadata)
.Ignore(PdfDetail.Positions)
.ScrubDates()
.OnlyPages(1, 2));
Use(PdfProfile...)compares only what a test guards, such as what assistive technology uses.Ignore(PdfSection...)leaves out a whole section, such as a layout the design team changes freely.Ignore(PdfDetail...)leaves out one detail everywhere, such as positions, colors or the title.ScrubDates(),ScrubGuids()andScrubText(...)replace values that change on every run, such as print dates, permit numbers and booking IDs.OnlyPages(...)snapshots part of a long document, such as the summary on pages 3 and 4.Check(PdfCheckSet.Version1_0)records what the built-in checks find, so the findings are approved with the snapshot.
The snapshot's first lines always list what was ignored, so nobody reading it mistakes a missing section for an empty one.
Use a profile
Most tests guard one thing: the content, its accessibility or how the page looks. A
PdfProfile leaves out everything else in one call, and further Ignore calls leave
out more:
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
.Use(PdfProfile.Accessibility)
.Ignore(PdfDetail.Language));
The snapshot's header lists the sections and fields a profile leaves out, not the profile's name. If a later release changes what a profile leaves out, the header changes too, so the test fails and says so instead of quietly checking less.
Every profile, and what it leaves out, is listed in the reference.
Set options for the whole suite
A suite whose tests share options, such as a profile or scrubbers, can set them once with
Snapshot.DefaultPdfOptions, Snapshot.DefaultDocxOptions or Snapshot.DefaultTextOptions (for text and JSON).
Each call's own configure runs after the defaults and adds to them. A PDF or Word call can leave out more, but it
can't bring back what the defaults leave out (its Strict profile doesn't undo a default one), so keep the defaults
to what every test agrees on and set the rest in each test. A PDF or Word snapshot's first lines list what's ignored,
but not what's scrubbed. For text and JSON, a call's KeepDates() or KeepGuids() does undo a default scrub.
Snapshot.DefaultPdfOptions = options => options.Use(PdfProfile.Accessibility).ScrubDates();
Set them in a module initializer, as for Snapshot.Directory.
Leave out sections
Pass the sections to leave out. They print as (ignored) instead of their contents.
[Fact]
public async Task TrailMapTemplate_ShouldKeepItsTagStructure_WhenStylingChanges()
{
using Stream pdf = await ReportGenerator.CreateTrailMapAsync();
// This test guards the tag tree and the text, so leave out the metadata, the document settings
// and the layout (positions, fonts and colors), which the design team changes freely.
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
.Ignore(PdfSection.Metadata, PdfSection.Settings, PdfSection.Layout));
}
The snapshot then starts like this:
# pdfua-snapshot v1
# ignored sections: settings, metadata, layout
document
marked=yes lang=en-US display-doc-title=yes pages=2
settings
(ignored)
metadata
(ignored)
...
Every PdfSection is listed in the reference.
Leave out one detail everywhere
For something narrower than a section, ignore a PdfDetail. It's left out everywhere it appears, and
the rest of each line is still compared.
[Fact]
public async Task SeasonalGuide_ShouldMatchApprovedSnapshot_WhenGenerated()
{
using Stream pdf = await ReportGenerator.CreateSeasonalGuideAsync(month: 6);
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
// The title includes the month, so it changes every time.
.Ignore(PdfDetail.Title)
// Text reflows when the guide has more rows, so where things land doesn't matter here.
// Fonts, sizes and colors are still compared.
.Ignore(PdfDetail.Positions));
}
With PdfDetail.Positions ignored, a layout line such as
p1 text tagged at=72,720 width=44 font="Arial" size=18 color=gray:0 "Trails"
becomes
p1 text tagged font="Arial" size=18 color=gray:0 "Trails"
Every PdfDetail is listed in the reference.
Allow small numeric differences
Positions are rounded to whole points, and font sizes and line widths to 0.1 pt. A value close to a rounding boundary
can flip after a font or library update: text at 72.49 pt prints at=72,..., and moved by 0.02 pt it prints
at=73,.... Ignoring PdfDetail.Positions would also hide a heading that moved 40 pt, so tolerate the noise instead:
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
.Tolerate(PdfTolerance.Positions, points: 1)
.Tolerate(PdfTolerance.FontSizes, points: 0.5));
- A number that differs from the verified one by no more than its tolerance doesn't fail the test, and the verified file stays as it is, even in accept mode, so the baseline doesn't creep a point at a time.
Positionscovers where text, images, paths and annotations are, the size of images, paths and annotations, and the position a link or bookmark opens its page at.FontSizescovers a text run's size, andLineWidthsa path's width. Other fields aren't numbers and can't be tolerated, and neither can a layout attribute in theother-attributes=hash or a path'sshape=hash, so reshaping a path still fails the test.- The snapshot's header lists each tolerance (
# tolerances: positions 1pt, font-sizes 0.5pt), so a reader knows its numbers are approximate. Comparing two snapshots, withDocumentDiff.CompareSnapshotsorpdiffy, applies the tolerances their header lists.
Scrub values that change on every run
A generated document often prints today's date, a permit number or a GUID. Scrubbers replace those inside quoted text with a stable placeholder, so the rest of the text is still compared.
[Fact]
public async Task TrailPass_ShouldMatchApprovedSnapshot_WhenGenerated()
{
using Stream pdf = await ReportGenerator.CreateTrailPassAsync();
await Snapshot.VerifyPdfAsync(pdf, configure: options => options
// "Printed 10/27/2026" becomes "Printed Date_1". The same text always gets the same number,
// so the snapshot still shows that the print date and the issue date match.
.ScrubDates()
// Booking IDs become Guid_1, Guid_2 and so on.
.ScrubGuids()
// Anything else that changes, with your own pattern. "$1" keeps the first group.
.ScrubText(new Regex(@"(Permit) #\d+"), "$1 #<number>"));
}
Note
Scrubbers only touch quoted text: element text, alt text, titles, link addresses, form values, bookmark titles and so on. Tag types, keys and numbers are never changed.
ScrubDates() recognizes 2026-10-27, 10/27/2026, October 27, 2026 and 27 Oct 2026, each with an optional time,
and PDF dates such as D:20261027120000.
Snapshot only some pages
For a long document where a test is about one part, keep only those pages:
[Fact]
public async Task AnnualReport_ShouldKeepTheSummaryTable_WhenGenerated()
{
using Stream pdf = await ReportGenerator.CreateAnnualReportAsync();
// The summary table is on pages 3 and 4; the other 120 pages have their own tests.
await Snapshot.VerifyPdfAsync(pdf, configure: options => options.OnlyPages(3, 4));
}
- A structure element or bookmark stays when it, or anything under it, is on one of those pages.
- The page count, page labels and fonts still describe the whole file.
- Asking for a page past the end throws.
Word documents
DOCX snapshots have their own sections and details, with the same scrubbers:
await Snapshot.VerifyDocxAsync(docx, configure: options => options
.Ignore(DocxSection.Styles)
.Ignore(DocxDetail.FieldResults, DocxDetail.RevisionAuthors)
.ScrubDates());
DocxProfile:Use(DocxProfile.Content)compares the text and its structure without page setup, styles or formatting details. See Word profiles.DocxSection:Document,Sections,Styles,Numbering,Body,Headers,Footers,Notes, andChecks, printed only withCheck.DocxDetail: image hashes, field results, fonts, font sizes, colors, spacing, indents, revision and comment authors, custom properties, the document's author, and other formatting. See the reference for exactly what each leaves out.- DOCX scrubbers also apply to link addresses.
Tip
DocxDetail.FieldResults is the one most Word tests want: a PAGE or DATE field, or a table of contents, stores the
last result Word computed, which changes whenever the document is laid out again.
Text and JSON
Text and JSON snapshots scrub the whole text rather than quoted values. See Text and JSON.
Name a snapshot
The options that leave out and scrub content also name a snapshot and choose its folder: o => o.Variant(trail) gives each row of a theory its own
snapshot. See Naming and folders.
See also
- Options at a glance, every value in one place
PdfSnapshotOptions,DocxSnapshotOptions,TextSnapshotOptions