How to Test Gatsby Websites
Build a Gatsby testing strategy with Jest, query-aware component tests, browser tests, CI, and accessibility checks. Includes setup guidance, examples, and troubleshooting.
Test a Gatsby website at several levels: use Jest and React Testing Library for isolated components, provide representative data to components that depend on Gatsby GraphQL queries, use Cypress or Playwright for important browser journeys, and run accessibility checks alongside manual keyboard and visual review. In CI, build the site with gatsby build, serve that production build with gatsby serve, and run browser tests against it when you need confidence closer to deployment.
Gatsby does not configure unit testing out of the box. Its official guides describe Jest setup, Cypress end-to-end testing, and accessibility practices. Follow the setup that matches your Gatsby version and package manager; check the current Gatsby documentation before copying version-sensitive configuration.
1. Choose a test pyramid for Gatsby
Different tests catch different failures. Keep fast, focused checks at the bottom of the pyramid and reserve browser tests for behavior that needs a real browser.
| Layer | What it checks | Typical tool | Trade-off |
|---|---|---|---|
| Unit and component | Rendering, event handling, conditional states, and component behavior | Jest and React Testing Library | Fast and focused, but does not prove the full site works in a browser. |
| Gatsby query-dependent components | Components that consume static or page query data | Jest plus representative Gatsby query data | Addresses Gatsby data inputs; stored data needs deliberate refreshing or snapshot management. |
| End-to-end (E2E) | Navigation, forms, search, links, and other complete user journeys | Cypress or Playwright | Higher confidence in browser behavior, with more setup and maintenance. |
| Accessibility | Known rule violations and user-facing access patterns | axe-powered checks plus manual review | Automated scans catch some issues, but cannot establish that a site is fully accessible. |
Use component tests for lots of state variations. Add a small set of E2E tests for consequential journeys. Treat accessibility as a check across the layers, not as a separate claim that automation can certify.
2. Set up Jest and React Testing Library
Gatsby’s unit-testing guide assumes Jest 29 or newer and documents additional Babel configuration because Gatsby’s transforms differ from a standard React project. A typical setup uses jest, babel-jest, babel-preset-gatsby, and identity-obj-proxy, with React Testing Library for user-oriented component tests.
Install the test dependencies
npm install --save-dev jest babel-jest babel-preset-gatsby identity-obj-proxy @testing-library/react @testing-library/jest-dom
Use compatible versions for your Gatsby and Node.js versions. If your project uses Yarn or pnpm, install the same packages with that package manager.
Configure Jest
Add a Jest configuration to package.json, or put the equivalent configuration in a jest.config.js file. This example maps styles and common static assets to mocks, loads a setup file, ignores Gatsby’s generated cache, and allows selected Gatsby dependencies to be transformed.
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
},
"jest": {
"transform": {
"^.+\\.[jt]sx?$": "babel-jest"
},
"transformIgnorePatterns": [
"node_modules/(?!gatsby|gatsby-script|gatsby-link|gatsby-plugin-utils|@gatsbyjs/reach-router)"
],
"moduleNameMapper": {
"\\.(css|scss|sass)$": "identity-obj-proxy",
"\\.(jpg|jpeg|png|gif|svg|webp)$": "<rootDir>/__mocks__/file-mock.js"
},
"setupFilesAfterEnv": ["<rootDir>/jest.setup.js"],
"testPathIgnorePatterns": ["node_modules", "\\.cache"]
}
}
Use a Babel preset aligned with Gatsby’s transforms. Gatsby documents babel-preset-gatsby for this purpose. If your repository already has Babel or Jest configuration, merge the relevant settings instead of replacing project-specific transforms.
Create the mapped asset mock and setup file:
// __mocks__/file-mock.js
module.exports = "test-file-stub";
// jest.setup.js
import "@testing-library/jest-dom";
Write tests that assert what a visitor can see or do, rather than asserting implementation details:
// src/components/SubscribeButton.test.js
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import SubscribeButton from "./SubscribeButton";
test("shows confirmation after subscribing", async () => {
const user = userEvent.setup();
render(<SubscribeButton />);
await user.click(screen.getByRole("button", { name: /subscribe/i }));
expect(screen.getByRole("status")).toHaveTextContent(/subscribed/i);
});
If the example uses @testing-library/user-event, install it too:
npm install --save-dev @testing-library/user-event
Run a single test file with npx jest src/components/SubscribeButton.test.js, or run the suite with npm test.
When Jest fails to parse Gatsby dependencies
Jest commonly skips transforming files in node_modules. Gatsby and some related packages may need transformation. If an error points to import, export, or another syntax feature in a Gatsby dependency, review transformIgnorePatterns and the Babel preset. Add only the packages your error identifies; an overly broad transform exception can slow the suite.
3. Test components that use Gatsby GraphQL data
A Gatsby page or component that reads GraphQL data needs that data in its test environment. A component test that omits query results can fail for reasons unrelated to the behavior you meant to test—or pass against an unrealistic empty state.
One Gatsby-specific option is the community package gatsby-plugin-testing. Its documented workflow is to add the plugin, run gatsby build or gatsby develop to save query results, then run tests. It stores static query data in .testing-static-queries.json; the plugin documentation says this file can be ignored by Git. Check the plugin’s current maintenance and compatibility with your Gatsby version before adopting it.
- Add and configure the plugin according to its current documentation.
- Run a Gatsby build or development server so the plugin can collect query results.
- Run Jest and confirm the tested component receives the expected query data.
- After editing a query, rebuild the data before trusting the tests.
Stale saved query data is a particularly misleading edge case: tests can keep using old results after a query changes. The plugin also documents snapshots that freeze query inputs and can allow tests to run without a Gatsby build. Snapshots improve repeatability, but update them intentionally when the expected query data changes.
For components where the query plugin is unnecessary, pass data through props or a small test fixture at the component boundary. That keeps the test focused and avoids bringing the whole Gatsby build pipeline into a simple rendering test.
4. Add browser end-to-end tests
Use E2E tests for behavior that depends on the assembled site and browser: a visitor following navigation, submitting a form, using search or filters, or interacting with a menu or modal. Gatsby’s official walkthrough focuses on Cypress and describes Playwright as a popular alternative.
For a Cypress development loop, Gatsby documents using start-server-and-test to start gatsby develop, wait for the local site, and launch Cypress. Install the runner and helper using the current Gatsby/Cypress instructions, then add a script shaped like this to package.json:
{
"scripts": {
"e2e:open": "start-server-and-test develop http://localhost:8000 cypress:open",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
}
}
This assumes your Gatsby development server uses port 8000 and that the corresponding scripts and Cypress configuration exist. Adjust the URL and command names to match your project. An example test might verify a generated page and a meaningful navigation action:
// cypress/e2e/navigation.cy.js
describe("site navigation", () => {
it("opens an article from the home page", () => {
cy.visit("/");
cy.findByRole("link", { name: /latest article/i }).click();
cy.location("pathname").should("match", /articles/);
cy.findByRole("main").should("be.visible");
});
});
The accessible query shown here requires Cypress Testing Library to be installed and configured. Without it, use Cypress’s built-in commands and selectors that are stable and meaningful for your application.
If the development server uses Gatsby’s --https option, Gatsby’s guide notes that start-server-and-test may wait indefinitely unless you set START_SERVER_AND_TEST_INSECURE=1. Apply that environment setting only for the local test workflow that needs it.
5. Test the production build in CI
Development mode is convenient while writing tests, but CI should exercise the generated production site when deployment-like behavior matters. Gatsby recommends building with gatsby build and serving with gatsby serve for this purpose. Run Cypress in non-interactive mode with cypress run, not cypress open.
A CI job’s central commands should run in this order:
npm ci
npm test -- --runInBand
npm run build
npm run serve &
npx wait-on http://localhost:9000
npx cypress run
This is a command sequence, not a complete CI provider configuration. Gatsby’s production server commonly uses port 9000; confirm the port and start command for your project. The CI environment also needs the browser dependencies required by your chosen E2E runner. Ensure the background server is stopped or the job environment is cleaned up after the tests.
Keep the CI suite focused. Run unit tests and build checks on each change; include a small set of high-value browser journeys. Broader browser coverage can run on a schedule or before release if its runtime and maintenance cost would otherwise slow routine changes.
6. Add accessibility checks and manual review
Gatsby includes eslint-plugin-jsx-a11y warnings by default, which can flag some issues while you write JSX. Linting does not inspect every runtime state or prove that the rendered page is usable. Gatsby’s E2E guidance describes adding axe-powered checks with cypress-axe; run those checks on representative pages and states.
Pair automated scans with manual checks. For key pages and flows, verify:
- Every action can be reached and used with a keyboard, and focus remains visible.
- Headings and landmarks communicate a sensible page structure.
- Forms have understandable labels and their errors are conveyed accessibly.
- Text and interface colors have adequate contrast.
- Content remains usable at zoom and with magnification.
- Images and media have suitable text alternatives where needed.
- Menus, dialogs, and custom widgets can be operated and dismissed as expected.
Automated accessibility scans detect violations from a known ruleset; they cannot infer every user’s needs or whether a flow makes sense. Treat them as repeatable regression checks, then manually test interactions and content.
7. Test visual output when appearance matters
Functional assertions do not catch every visual regression. If layout, typography, or responsive rendering is important, capture representative pages at the viewport sizes that matter and compare the resulting images through your chosen review process. Keep the route, viewport, device scale, fonts, and loaded content consistent between captures; otherwise dynamic content can create noisy differences.
For a local browser-based capture workflow, use your E2E runner to open the built page and save a screenshot. Cypress supports screenshot capture through its browser test workflow; consult the current Cypress documentation for command behavior and configuration. For screenshots of publicly reachable pages or a capture service in an automated workflow, ScreenshotNeo can return an image from one GET request. A screenshot is a visual aid, not a replacement for assertions about links, forms, accessibility, or site behavior.
8. Troubleshooting Gatsby tests
| Symptom | Likely cause | What to do |
|---|---|---|
| Jest reports unexpected token or cannot parse a Gatsby package | A Gatsby dependency was skipped by Jest transforms or Babel is not using Gatsby’s preset. | Align Babel with babel-preset-gatsby and adjust transformIgnorePatterns for the dependency named in the error. |
| CSS or image imports fail in a component test | Jest does not know how to load non-JavaScript assets. | Map styles to identity-obj-proxy and images/fonts to a file mock. |
| A query-dependent component has missing or old data | Query results were not generated, or stored static-query data is stale. | Run the plugin’s build/develop data collection again after query edits, or update the deliberate test snapshot. |
| E2E runner starts before the site is ready | The test command did not wait for the server URL or is checking the wrong port. | Use a server-wait helper, confirm Gatsby’s port, and check the server log for build errors. |
| HTTPS test startup waits indefinitely | The local server uses Gatsby’s HTTPS option and the helper rejects its certificate. | For that local workflow, set START_SERVER_AND_TEST_INSECURE=1 as Gatsby documents. |
| Tests pass in develop but fail after deployment build | The production bundle, generated routes, or production-only behavior differs from development. | Run gatsby build, serve that output with gatsby serve, and execute the browser suite against it. |
| Accessibility scan passes but keyboard use is broken | The scan cannot establish whether the interaction is operable or understandable. | Manually test keyboard access, focus visibility, dialogs, menus, forms, zoom, and content semantics. |
9. Keep tests fast and dependable
- Use the right scope. Put detailed state coverage in component tests and reserve E2E for integration journeys.
- Control inputs. Fix test data, route, viewport, and relevant browser state so failures are reproducible.
- Refresh generated query data. A stale fixture can make a test suite appear healthy while checking old assumptions.
- Wait for meaningful conditions. In browser tests, wait for a visible result or stable page state rather than arbitrary delays where possible.
- Limit external dependencies. Third-party APIs and changing content can make CI flaky; provide stable fixtures or controlled test endpoints where appropriate.
- Watch the cost of browser coverage. E2E tests take more setup and maintenance than isolated tests. Add them where browser integration changes the confidence you need.
Testing has an engineering cost in dependencies, CI time, and ongoing maintenance. A focused suite is easier to trust than a large set of overlapping browser checks. No benchmark or universal test count applies to every Gatsby project; choose coverage based on the routes and behaviors whose failure matters to your users.
Or skip the browser setup
For a screenshot of a public page, ScreenshotNeo provides a one-call capture API. The example below saves a WebP response as shot.webp. Keep the API key private and follow the ScreenshotNeo API documentation for request options and response headers.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the request was billed. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. See ScreenshotNeo for product details, and sign up free.
Frequently asked questions
Does Gatsby include a built-in unit test command?
Gatsby does not include unit testing support out of the box. Configure a test runner such as Jest and use the Gatsby-compatible transform setup.
Should every Gatsby page have an E2E test?
Usually, no. Cover critical shared behavior and representative routes in the browser, then use faster component tests for detailed variations.
Can an axe scan prove my site is accessible?
No. It can identify certain known rule violations. Manual keyboard, focus, zoom, semantic, and interaction checks are still necessary.
Should CI test the development server or production build?
Use the development server for a convenient authoring loop. Test the production build with gatsby build and gatsby serve when you need deployment-like confidence.
Primary references
- Gatsby documentation: Unit Testing, including the Jest and Babel setup guidance.
- Gatsby documentation: End-to-End Testing, including Cypress, production builds, and axe checks.
- Gatsby documentation: Accessibility checklist.
- Cypress documentation: End-to-end testing and accessibility testing guidance.
gatsby-plugin-testingdocumentation: static query data and snapshots.


