facebook/react
Testing
The runtime test suite is Jest, with substantial wrapping, environment configuration, and feature-flag handling provided by scripts/jest/. Tests run hundreds of thousands of times across release channels in CI; learning the gating system is the difference between "I added a test for my fix" and "my test was silently skipped on the channel I cared about".
Running tests
yarn test # default: experimental channel, source mode
yarn test-stable # stable channel
yarn test-www # www-modern
yarn test-classic # www-classic
yarn test --build # run against build/ bundles instead of source
yarn test packages/react-reconciler/... # focused
yarn test -t "useState" # pattern by description
yarn test-build-devtools # DevTools-specific run, experimental channelThe runner is scripts/jest/jest-cli.js, which translates flags to the correct Jest config under scripts/jest/config.source*.js or scripts/jest/config.build*.js.
Where tests live
Each package has a src/__tests__/ directory next to its source. The reconciler — being renderer-agnostic — has the largest set of tests, all written against react-noop-renderer so they exercise the work loop without DOM dependencies. Examples:
packages/react-reconciler/src/__tests__/ReactHooks-test.jspackages/react-reconciler/src/__tests__/ReactSuspense-test.internal.jspackages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.jspackages/react-server-dom-webpack/src/__tests__/packages/react-devtools-shared/src/__tests__/store-test.js
Files ending -test.internal.js may use jest.mock to flip private feature flags. Files ending -test.js should not.
Feature-flag gating
Every test runs against every release channel by default. To make a test conditional on a flag, use the @gate pragma or the gate runtime helper. Both are set up in scripts/jest/setupTests.js.
// @gate enableNewFeature
it('does something the new way', () => {
/* ... */
});
// Inside a test:
test('mixed', () => {
if (gate((flags) => flags.enableNewFeature)) {
// expect new behavior
} else {
// expect old behavior
}
});@gate experimental and @gate !www are also supported. When a test is gated off, Jest still runs it — but reverses the pass/fail expectation, ensuring the gated code path is never silently skipped on a channel where it should run.
Writing renderer-agnostic tests
When fixing a reconciler bug, prefer react-noop-renderer:
const Scheduler = require('scheduler/unstable_mock');
const ReactNoop = require('react-noop-renderer');
const React = require('react');
it('schedules an update', async () => {
ReactNoop.render(<MyComponent />);
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput(<span>hi</span>);
});The helpers waitForAll, waitForPaint, waitFor, etc. come from internal-test-utils (packages/internal-test-utils/). They wrap act and the scheduler's mock to give you a synchronous-feeling test of an async render.
Snapshot rules
The runtime largely avoids Jest snapshot tests in favor of explicit assertions: toEqual, toMatchRenderedOutput, etc. The compiler is the opposite — it leans heavily on golden-file snapshots in compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/ and uses its own runner (yarn snap from inside compiler/).
Update compiler snapshots:
cd compiler
yarn snap -u # update everything
yarn snap -u -p simple # update just files matching the patternTesting across renderers
Some bugs only manifest in react-dom. The DOM test files live in packages/react-dom/src/__tests__/, and they use jsdom (configured in scripts/jest/preprocessor.js and friends). For SSR/Fizz tests, look in packages/react-dom/src/__tests__/ReactDOMFizz*-test.js.
Server Components are tested in:
packages/react-server-dom-webpack/src/__tests__/packages/react-server-dom-parcel/src/__tests__/packages/react-server-dom-turbopack/src/__tests__/packages/react-server-dom-unbundled/src/__tests__/
These are bundler-aware tests and use Jest module mocks to stand in for the bundler's manifest.
Mocking time and the scheduler
The scheduler comes with a unstable_mock implementation that lets a test step the work loop one frame at a time:
const Scheduler = require('scheduler/unstable_mock');
Scheduler.unstable_advanceTime(100);
Scheduler.unstable_flushAll();
expect(Scheduler).toFlushAndYield(['A', 'B']);This is essential for testing concurrent features (transitions, deferred values, suspense retries). All the tests in packages/react-reconciler/src/__tests__/ use it.
Fuzz tests
yarn test-fuzz runs packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js and a couple of sibling files. CI runs them on a separate workflow (runtime_fuzz_tests.yml) so a long fuzz run doesn't block normal PRs.
Common gotchas
actwarnings. If you forget to wrap an update inactin areact-domtest, the warning is loud. UsewaitFor*frominternal-test-utilsto keep things tidy.- Channel-specific failures. A test that passes locally on experimental can fail on stable if it relies on a flag-gated API. Always run the appropriate
yarn test-stable/yarn test-www/yarn test-classicbefore assuming you're done. -test.internal.jsvs-test.js. Internal tests canjest.mockprivate feature flags. Non-internal tests cannot — and forgetting that is the most common cause of "I can't get my test to fail".
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.