October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

How to Run BackstopJS Visual Tests in GitLab CI

Configure BackstopJS visual regression tests in GitLab CI, publish JUnit results, preserve failure artifacts, and make sure failed comparisons actually fail the job.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install BackstopJS in your project, keep its approved reference screenshots under version control, make the app reachable from the GitLab runner, and run npx backstop test in a CI job. Enable BackstopJS’s CI report and publish its JUnit XML with GitLab’s artifacts:reports:junit. The test command’s exit status—not the report upload—must fail the job when comparisons fail.

How the integration works

BackstopJS captures configured pages and compares them with approved reference screenshots. Its workflow is init, test, then approve: approval promotes the latest test captures into the baseline used by future runs. Treat approval as a reviewed change, not an automatic reaction to every failed CI run. See the BackstopJS README.

GitLab can ingest BackstopJS’s JUnit XML and show test results in pipeline and merge request views. However, GitLab explicitly says JUnit report artifacts do not determine job status: “Unit test reports require the JUnit XML format and do not affect job status. To make a job fail when tests fail, your job’s script must exit with a non-zero status.” See GitLab’s unit test reports documentation.

Prepare BackstopJS and its baselines

  1. Add the dependency. Install BackstopJS as a project dependency and commit the lockfile. The package metadata for version 6.3.25 lists Node.js 16 or later and npm 8 or later; use the version pinned by your own lockfile to choose a compatible CI image. Check the BackstopJS package metadata and your installed version’s documentation for version-sensitive behavior.
  2. Initialize and configure it locally. Run npx backstop init. Add at least one viewport and one or more scenarios; each scenario needs a label and URL. The URL must resolve from the runner’s network context in CI, not merely from your laptop.
  3. Create references deliberately. Run the initial capture and review the screenshots before establishing the approved reference set. Commit those reference files, or otherwise make the approved set available to the test job. Review later baseline updates as code changes.
  4. Make the application available to the test job. Build or start the site before the visual test runs, or connect the job to an environment where it is already running. If another job builds or deploys the app, arrange job order and network access accordingly. The correct host name and route depend on your GitLab runner and deployment setup.

Configure a GitLab CI job and JUnit report

Enable BackstopJS’s CI report in its configuration, for example with "report": ["CI"]. The README documents a default JUnit filename of xunit.xml and lets you customize the report directory, filename, and test suite name. Set GitLab’s report path to the actual output location. For example, configure paths.ci_report as backstop_data/ci_report, then use:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
visual_regression:
  stage: test
  script:
    - npm ci
    - npm run build
    # Start the application or connect to it here; it must be reachable by this job.
    - npx backstop test
  artifacts:
    when: always
    paths:
      - backstop_data/ci_report/
    reports:
      junit: backstop_data/ci_report/xunit.xml

This is a starting pattern, not a drop-in configuration: supply the project’s actual build and app-start steps, a Node image compatible with the pinned BackstopJS version, and the report path configured for the project. Including the report directory under artifacts:paths makes its files browsable as artifacts; artifacts:when: always requests upload even when the job fails. GitLab’s JUnit setting accepts an XML filename, glob, or array of report paths, not a directory by itself. The XML filename must end in .xml. GitLab documents limits of less than 30 MB per report file and less than 100 MB total per job; duplicate test names are ignored after the first occurrence. Consult GitLab’s report documentation for current behavior.

Check the failure gate

Keep npx backstop test in the job’s script and verify the pinned BackstopJS version returns a non-zero exit code on a visual-test failure. Do not infer that the job is a merge gate merely because GitLab displays a failed JUnit test or uploads an artifact: report ingestion is separate from the command’s exit status.

Choose a rendering environment

Approach What to expect Trade-offs to check
Run directly in the CI job BackstopJS runs with the browser and dependencies available in the job’s environment. Differences between local and CI rendering may affect comparisons. Ensure the runner image has the required browser environment and that generated files are accessible to the job.
Use BackstopJS’s --docker option BackstopJS documents this option as a way to reduce rendering differences. It invokes Docker and uses a versioned BackstopJS image by default. The runner must be able to use Docker; check container permissions, file ownership, artifact access, and app networking. For CI-style piped output, the README notes removing -t from the default Docker command template.

Docker is an option, not a requirement. BackstopJS’s README warns that in the cited Mac/Windows setup, localhost does not reach the host from its Docker rendering environment and suggests host.docker.internal there. That hostname is not a universal GitLab runner solution: verify the actual runner’s network route to the app before copying it. See the BackstopJS README.

Make visual failures easier to inspect

GitLab’s JUnit report gives structured test feedback, while job logs remain useful for diagnosing command and setup failures. To retain evidence after a failed comparison, upload the report and relevant screenshot files as artifacts with artifacts:when: always. GitLab also documents JUnit system-out attachment tags for screenshot attachments; upload the associated screenshot files as artifacts so they are available to reviewers. Follow the formatting requirements in GitLab’s unit test report documentation.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common CI failures

  • The test cannot open a scenario URL: The URL may work on your machine but not from the runner. Confirm the app is running before the test and use a hostname and port reachable in that job’s network context.
  • Docker rendering cannot reach the app: A container’s localhost may refer to the container itself, not the host or another service. Configure a route appropriate to the runner. The BackstopJS README’s host.docker.internal note applies to its cited Mac/Windows setup, not every CI environment.
  • The report is missing from GitLab: Confirm CI reporting is enabled, that paths.ci_report and the generated filename match the YAML path, and that the XML file exists when the job ends. Use an XML filename—not only a directory—in reports:junit.
  • The job succeeds despite a reported visual failure: GitLab does not fail jobs based on JUnit report contents. Check the test process’s exit code and the behavior of the version pinned in your lockfile.
  • Reports or screenshots disappear after a failed test: Use artifacts:when: always and include the output files under artifacts:paths as well as the JUnit report declaration.
  • Results differ between local runs and CI: Rendering environments can differ. Consider BackstopJS’s Docker option if the runner supports it, then verify browser dependencies, network access, and generated-file permissions.
  • JUnit results are incomplete: Check the XML extension, documented file-size limits, total report size, and duplicate test names. GitLab ignores duplicate test names after their first occurrence.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For one-off screenshots or a separate screenshot workflow, ScreenshotNeo offers a website screenshot API and MCP server. Its API can return an image or PDF; it is not a replacement for BackstopJS’s approved-reference comparison and CI failure gate. A one-call capture looks like this:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation. ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo’s free plan.

Frequently Asked Questions

Can BackstopJS use local scenario URLs?

Yes, but a local URL must resolve from the GitLab runner’s network context when the CI job runs.

Does GitLab require a particular JUnit report filename?

The report must be JUnit XML with an .xml extension; GitLab accepts a file path, glob, or array of paths.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.