Skip to content

Commit 4d626b6

Browse files
committed
test_runner: support test order randomization
1 parent ec88813 commit 4d626b6

22 files changed

+1382
-21
lines changed

doc/api/cli.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,38 @@ changes:
27622762
Configures the test runner to only execute top level tests that have the `only`
27632763
option set. This flag is not necessary when test isolation is disabled.
27642764

2765+
### `--test-random-seed`
2766+
2767+
<!-- YAML
2768+
added: REPLACEME
2769+
-->
2770+
2771+
Set the seed used to randomize test execution order. This applies to both test
2772+
file execution order and queued tests within each file. Providing this flag
2773+
enables randomization implicitly, even without `--test-randomize`.
2774+
2775+
The value must be an integer between `0` and `4294967295`.
2776+
2777+
This flag cannot be used with `--watch` or `--test-rerun-failures`.
2778+
2779+
### `--test-randomize`
2780+
2781+
<!-- YAML
2782+
added: REPLACEME
2783+
-->
2784+
2785+
Randomize test execution order. This applies to both test file execution order
2786+
and queued tests within each file. This can help detect tests that rely on
2787+
shared state or execution order.
2788+
2789+
The seed used for randomization is printed in the test summary and can be
2790+
reused with `--test-random-seed`.
2791+
2792+
For detailed behavior and examples, see
2793+
[randomizing tests execution order][].
2794+
2795+
This flag cannot be used with `--watch` or `--test-rerun-failures`.
2796+
27652797
### `--test-reporter`
27662798

27672799
<!-- YAML
@@ -3679,6 +3711,8 @@ one is included in the list below.
36793711
* `--test-isolation`
36803712
* `--test-name-pattern`
36813713
* `--test-only`
3714+
* `--test-random-seed`
3715+
* `--test-randomize`
36823716
* `--test-reporter-destination`
36833717
* `--test-reporter`
36843718
* `--test-rerun-failures`
@@ -4257,6 +4291,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
42574291
[preloading asynchronous module customization hooks]: module.md#registration-of-asynchronous-customization-hooks
42584292
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
42594293
[running tests from the command line]: test.md#running-tests-from-the-command-line
4294+
[randomizing tests execution order]: test.md#randomizing-tests-execution-order
42604295
[scavenge garbage collector]: https://v8.dev/blog/orinoco-parallel-scavenger
42614296
[security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
42624297
[semi-space]: https://www.memorymanagement.org/glossary/s.html#semi.space

doc/api/test.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,94 @@ prevent shell expansion, which can reduce portability across systems.
585585
node --test "**/*.test.js" "**/*.spec.js"
586586
```
587587

588+
### Randomizing tests execution order
589+
590+
<!-- YAML
591+
added: REPLACEME
592+
-->
593+
594+
> Stability: 1.0 - Early development
595+
596+
The test runner can randomize execution order to help detect
597+
order-dependent tests. When enabled, the runner randomizes both discovered
598+
test files and queued tests within each file. Use `--test-randomize` to
599+
enable this mode.
600+
601+
```bash
602+
node --test --test-randomize
603+
```
604+
605+
When randomization is enabled, the test runner prints the seed used for the run
606+
as a diagnostic message:
607+
608+
```text
609+
Randomized test order seed: 12345
610+
```
611+
612+
Use `--test-random-seed=<number>` to replay the same randomized order
613+
deterministically. Supplying `--test-random-seed` also enables randomization,
614+
so `--test-randomize` is optional when a seed is provided:
615+
616+
```bash
617+
node --test --test-randomize --test-random-seed=12345
618+
```
619+
620+
In most test files, randomization works automatically. One important exception
621+
is when subtests are awaited one by one. In that pattern, each subtest starts
622+
only after the previous one finishes, so the runner keeps declaration order
623+
instead of randomizing it.
624+
625+
Example: this runs sequentially and is **not** randomized.
626+
627+
```mjs
628+
import test from 'node:test';
629+
630+
test('math', async (t) => {
631+
for (const name of ['adds', 'subtracts', 'multiplies']) {
632+
// Sequentially awaiting each subtest preserves declaration order.
633+
await t.test(name, async () => {});
634+
}
635+
});
636+
```
637+
638+
```cjs
639+
const test = require('node:test');
640+
641+
test('math', async (t) => {
642+
for (const name of ['adds', 'subtracts', 'multiplies']) {
643+
// Sequentially awaiting each subtest preserves declaration order.
644+
await t.test(name, async () => {});
645+
}
646+
});
647+
```
648+
649+
Using suite-style APIs such as `describe()`/`it()` or `suite()`/`test()`
650+
still allows randomization, because sibling tests are queued together.
651+
652+
Example: this remains eligible for randomization.
653+
654+
```mjs
655+
import { describe, it } from 'node:test';
656+
657+
describe('math', () => {
658+
it('adds', () => {});
659+
it('subtracts', () => {});
660+
it('multiplies', () => {});
661+
});
662+
```
663+
664+
```cjs
665+
const { describe, it } = require('node:test');
666+
667+
describe('math', () => {
668+
it('adds', () => {});
669+
it('subtracts', () => {});
670+
it('multiplies', () => {});
671+
});
672+
```
673+
674+
`--test-randomize` and `--test-random-seed` are not supported with `--watch` mode.
675+
588676
Matching files are executed as test files.
589677
More information on the test file execution can be found
590678
in the [test runner execution model][] section.
@@ -625,6 +713,10 @@ test runner functionality:
625713
* `--test-reporter` - Reporting is managed by the parent process
626714
* `--test-reporter-destination` - Output destinations are controlled by the parent
627715
* `--experimental-config-file` - Config file paths are managed by the parent
716+
* `--test-randomize` - Randomization is managed by the parent process and
717+
propagated to child processes
718+
* `--test-random-seed` - Randomization seed is managed by the parent process and
719+
propagated to child processes
628720

629721
All other Node.js options from command line arguments, environment variables,
630722
and configuration files are inherited by the child processes.
@@ -1531,6 +1623,13 @@ changes:
15311623
that specifies the index of the shard to run. This option is _required_.
15321624
* `total` {number} is a positive integer that specifies the total number
15331625
of shards to split the test files to. This option is _required_.
1626+
* `randomize` {boolean} Randomize execution order for test files and queued tests.
1627+
This option is not supported with `watch: true`.
1628+
**Default:** `false`.
1629+
* `randomSeed` {number} Seed used when randomizing execution order. If this
1630+
option is set, runs can replay the same randomized order deterministically,
1631+
and setting this option also enables randomization.
1632+
**Default:** `undefined`.
15341633
* `rerunFailuresFilePath` {string} A file path where the test runner will
15351634
store the state of the tests to allow rerunning only the failed tests on a next run.
15361635
see \[Rerunning failed tests]\[] for more information.

doc/node-config-schema.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,14 @@
528528
"type": "boolean",
529529
"description": "run tests with 'only' option set"
530530
},
531+
"test-random-seed": {
532+
"type": "number",
533+
"description": "seed used to randomize test execution order"
534+
},
535+
"test-randomize": {
536+
"type": "boolean",
537+
"description": "run tests in a random order"
538+
},
531539
"test-reporter": {
532540
"oneOf": [
533541
{
@@ -910,6 +918,14 @@
910918
"type": "boolean",
911919
"description": "run tests with 'only' option set"
912920
},
921+
"test-random-seed": {
922+
"type": "number",
923+
"description": "seed used to randomize test execution order"
924+
},
925+
"test-randomize": {
926+
"type": "boolean",
927+
"description": "run tests in a random order"
928+
},
913929
"test-reporter": {
914930
"oneOf": [
915931
{

doc/node.1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,22 @@ tests must satisfy \fBboth\fR requirements in order to be executed.
13571357
Configures the test runner to only execute top level tests that have the \fBonly\fR
13581358
option set. This flag is not necessary when test isolation is disabled.
13591359
.
1360+
.It Fl -test-random-seed
1361+
Set the seed used to randomize test execution order.
1362+
This applies to both test file execution order and queued tests within each file.
1363+
Providing this flag enables randomization implicitly, even without
1364+
\fB--test-randomize\fR.
1365+
The value must be an integer between 0 and 4294967295.
1366+
This flag cannot be used with \fB--watch\fR or \fB--test-rerun-failures\fR.
1367+
.
1368+
.It Fl -test-randomize
1369+
Randomize test execution order.
1370+
This applies to both test file execution order and queued tests within each file.
1371+
This can help detect tests that rely on shared state or execution order.
1372+
The seed used for randomization is printed in the test summary and can be
1373+
reused with \fB--test-random-seed\fR.
1374+
This flag cannot be used with \fB--watch\fR or \fB--test-rerun-failures\fR.
1375+
.
13601376
.It Fl -test-reporter
13611377
A test reporter to use when running tests. See the documentation on
13621378
test reporters for more details.
@@ -2034,6 +2050,10 @@ one is included in the list below.
20342050
.It
20352051
\fB--test-reporter-destination\fR
20362052
.It
2053+
\fB--test-randomize\fR
2054+
.It
2055+
\fB--test-random-seed\fR
2056+
.It
20372057
\fB--test-reporter\fR
20382058
.It
20392059
\fB--test-rerun-failures\fR

0 commit comments

Comments
 (0)