Table of Contents

Continuous integration

Snapshot tests run in CI like any other test: dotnet test fails when a snapshot differs. Three things make a failed run easy to act on: keep what the run received, publish the HTML reports, and summarize the run on the job page.

The examples run pdiffy as a local tool, pinned in the repository's tool manifest to the same version as the PDiffy package (see Command line), so the summary reads documents as the tests did.

What PDiffy does on a build server

  • Accept mode is ignored. When CI, TF_BUILD, JENKINS_URL or TEAMCITY_VERSION is set, as GitHub Actions, GitLab, Bitbucket, Azure DevOps, Jenkins and TeamCity do, PDIFFY_ACCEPT=1 has no effect, so a pipeline can never approve its own snapshots.
  • One snapshot for every OS. A snapshot is the same text on Windows and Linux, so one verified file serves a matrix build.
  • Deterministic builds. If your build maps source paths (ContinuousIntegrationBuild, common on CI), the test file's path doesn't exist on disk. PDiffy finds the test file's folder in the test project instead, using the project folder PDiffy's build file recorded. To keep snapshots in one folder, use Snapshot.ProjectFolder (see Getting started).
  • New snapshots. A new snapshot on a build server usually means the verified file wasn't committed, or the snapshot folder doesn't point into the checkout, and the failure says so.

GitHub Actions

This workflow runs the tests, and when they fail, adds a table of every changed snapshot to the job summary and keeps the received files and reports as an artifact:

name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x

      - run: dotnet test

      # Only after a failure: summarize the changed snapshots and keep them.
      - name: Summarize snapshot changes
        if: failure()
        run: |
          dotnet tool restore
          dotnet pdiffy report --all --markdown "$GITHUB_STEP_SUMMARY" --html snapshot-summary.html
      - name: Keep received snapshots and reports
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: snapshots
          path: |
            **/*.received.*
            snapshot-summary.html

The job summary then shows a table like this:

Snapshot Status Changes By section Can affect accessibility
tests/Snapshots/TrailMap.received.pdfua.txt changed 3: 1 moved, 1 attribute, 1 text structure 3 2
tests/Snapshots/Guide.received.docx.txt new

Download the artifact to open each report, or snapshot-summary.html, which links them all. Each report looks like the sample report.

Note

pdiffy report exits with 1 when it finds received snapshots. After if: failure() that doesn't matter, since the job has already failed.

Azure DevOps

steps:
  - task: UseDotNet@2
    inputs:
      version: 10.0.x

  - script: dotnet test
    displayName: Test

  - script: |
      dotnet tool restore
      dotnet pdiffy report --all --markdown $(Agent.TempDirectory)/snapshots.md --html snapshot-summary.html
      echo "##vso[task.uploadsummary]$(Agent.TempDirectory)/snapshots.md"
    displayName: Summarize snapshot changes
    condition: failed()

  - publish: $(System.DefaultWorkingDirectory)
    artifact: snapshots
    condition: failed()

Narrow the published folder to your snapshots folder to keep the artifact small.

GitLab CI

test:
  image: mcr.microsoft.com/dotnet/sdk:10.0
  script:
    - dotnet test
  after_script:
    - dotnet tool restore
    - dotnet pdiffy report --all --html snapshot-summary.html || true
  artifacts:
    when: on_failure
    paths:
      - "**/*.received.*"
      - snapshot-summary.html

Bitbucket Pipelines

The after-script runs whether the tests passed or not, so it checks BITBUCKET_EXIT_CODE and only writes the summary page after a failure. capture-on: failed keeps the received files and reports only when the step fails:

image: mcr.microsoft.com/dotnet/sdk:10.0

pipelines:
  default:
    - step:
        name: Test
        script:
          - dotnet test
        after-script:
          - |
            if [ "$BITBUCKET_EXIT_CODE" -ne 0 ]; then
              dotnet tool restore
              dotnet pdiffy report --all --html snapshot-summary.html || true
            fi
        artifacts:
          - name: Snapshots
            paths:
              - "**/*.received.*"
              - snapshot-summary.html
            capture-on: failed

Bitbucket has no job summary page, so open snapshot-summary.html from the step's Artifacts tab to reach each report. For the same reason, pdiffy findings can't write to a job summary here. When snapshots record checks, write the findings to a file and keep it with the reports: add dotnet pdiffy findings --all --markdown snapshot-findings.md || true to the after-script, after the pdiffy report line, and snapshot-findings.md to the artifact's paths.

Review and accept locally

CI shows what changed; accepting is done on a developer machine, so the change goes through code review:

  1. Run the failing tests locally, or download the artifact into the same folders.
  2. Review the reports, or run pdiffy report --all for a summary.
  3. Run pdiffy accept --all and commit the updated .verified.* files.

Now and then, run pdiffy coverage --all to see how many snapshots still compare each section, in case tests have drifted into ignoring the same thing everywhere, and pdiffy unused to find the snapshots of renamed or deleted tests. When snapshots record checks, pdiffy findings --all --markdown "$GITHUB_STEP_SUMMARY" puts a count of their problems, by rule, on the job page. In a pull request, --since origin/main adds how many problems the pull request fixed and introduced.

See also