Skip to content

Commit 0fb39fa

Browse files
committed
test_runner: support test order randomization
1 parent 4d0cb65 commit 0fb39fa

22 files changed

Lines changed: 1382 additions & 21 deletions

doc/api/cli.md

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

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

27752807
<!-- YAML
@@ -3687,6 +3719,8 @@ one is included in the list below.
36873719
* `--test-isolation`
36883720
* `--test-name-pattern`
36893721
* `--test-only`
3722+
* `--test-random-seed`
3723+
* `--test-randomize`
36903724
* `--test-reporter-destination`
36913725
* `--test-reporter`
36923726
* `--test-rerun-failures`
@@ -4265,6 +4299,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
42654299
[preloading asynchronous module customization hooks]: module.md#registration-of-asynchronous-customization-hooks
42664300
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
42674301
[running tests from the command line]: test.md#running-tests-from-the-command-line
4302+
[randomizing tests execution order]: test.md#randomizing-tests-execution-order
42684303
[scavenge garbage collector]: https://v8.dev/blog/orinoco-parallel-scavenger
42694304
[security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
42704305
[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
@@ -631,6 +631,94 @@ prevent shell expansion, which can reduce portability across systems.
631631
node --test "**/*.test.js" "**/*.spec.js"
632632
```
633633

634+
### Randomizing tests execution order
635+
636+
<!-- YAML
637+
added: REPLACEME
638+
-->
639+
640+
> Stability: 1.0 - Early development
641+
642+
The test runner can randomize execution order to help detect
643+
order-dependent tests. When enabled, the runner randomizes both discovered
644+
test files and queued tests within each file. Use `--test-randomize` to
645+
enable this mode.
646+
647+
```bash
648+
node --test --test-randomize
649+
```
650+
651+
When randomization is enabled, the test runner prints the seed used for the run
652+
as a diagnostic message:
653+
654+
```text
655+
Randomized test order seed: 12345
656+
```
657+
658+
Use `--test-random-seed=<number>` to replay the same randomized order
659+
deterministically. Supplying `--test-random-seed` also enables randomization,
660+
so `--test-randomize` is optional when a seed is provided:
661+
662+
```bash
663+
node --test --test-randomize --test-random-seed=12345
664+
```
665+
666+
In most test files, randomization works automatically. One important exception
667+
is when subtests are awaited one by one. In that pattern, each subtest starts
668+
only after the previous one finishes, so the runner keeps declaration order
669+
instead of randomizing it.
670+
671+
Example: this runs sequentially and is **not** randomized.
672+
673+
```mjs
674+
import test from 'node:test';
675+
676+
test('math', async (t) => {
677+
for (const name of ['adds', 'subtracts', 'multiplies']) {
678+
// Sequentially awaiting each subtest preserves declaration order.
679+
await t.test(name, async () => {});
680+
}
681+
});
682+
```
683+
684+
```cjs
685+
const test = require('node:test');
686+
687+
test('math', async (t) => {
688+
for (const name of ['adds', 'subtracts', 'multiplies']) {
689+
// Sequentially awaiting each subtest preserves declaration order.
690+
await t.test(name, async () => {});
691+
}
692+
});
693+
```
694+
695+
Using suite-style APIs such as `describe()`/`it()` or `suite()`/`test()`
696+
still allows randomization, because sibling tests are queued together.
697+
698+
Example: this remains eligible for randomization.
699+
700+
```mjs
701+
import { describe, it } from 'node:test';
702+
703+
describe('math', () => {
704+
it('adds', () => {});
705+
it('subtracts', () => {});
706+
it('multiplies', () => {});
707+
});
708+
```
709+
710+
```cjs
711+
const { describe, it } = require('node:test');
712+
713+
describe('math', () => {
714+
it('adds', () => {});
715+
it('subtracts', () => {});
716+
it('multiplies', () => {});
717+
});
718+
```
719+
720+
`--test-randomize` and `--test-random-seed` are not supported with `--watch` mode.
721+
634722
Matching files are executed as test files.
635723
More information on the test file execution can be found
636724
in the [test runner execution model][] section.
@@ -671,6 +759,10 @@ test runner functionality:
671759
* `--test-reporter` - Reporting is managed by the parent process
672760
* `--test-reporter-destination` - Output destinations are controlled by the parent
673761
* `--experimental-config-file` - Config file paths are managed by the parent
762+
* `--test-randomize` - Randomization is managed by the parent process and
763+
propagated to child processes
764+
* `--test-random-seed` - Randomization seed is managed by the parent process and
765+
propagated to child processes
674766

675767
All other Node.js options from command line arguments, environment variables,
676768
and configuration files are inherited by the child processes.
@@ -1579,6 +1671,13 @@ changes:
15791671
that specifies the index of the shard to run. This option is _required_.
15801672
* `total` {number} is a positive integer that specifies the total number
15811673
of shards to split the test files to. This option is _required_.
1674+
* `randomize` {boolean} Randomize execution order for test files and queued tests.
1675+
This option is not supported with `watch: true`.
1676+
**Default:** `false`.
1677+
* `randomSeed` {number} Seed used when randomizing execution order. If this
1678+
option is set, runs can replay the same randomized order deterministically,
1679+
and setting this option also enables randomization.
1680+
**Default:** `undefined`.
15821681
* `rerunFailuresFilePath` {string} A file path where the test runner will
15831682
store the state of the tests to allow rerunning only the failed tests on a next run.
15841683
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
@@ -524,6 +524,14 @@
524524
"type": "boolean",
525525
"description": "run tests with 'only' option set"
526526
},
527+
"test-random-seed": {
528+
"type": "number",
529+
"description": "seed used to randomize test execution order"
530+
},
531+
"test-randomize": {
532+
"type": "boolean",
533+
"description": "run tests in a random order"
534+
},
527535
"test-reporter": {
528536
"oneOf": [
529537
{
@@ -906,6 +914,14 @@
906914
"type": "boolean",
907915
"description": "run tests with 'only' option set"
908916
},
917+
"test-random-seed": {
918+
"type": "number",
919+
"description": "seed used to randomize test execution order"
920+
},
921+
"test-randomize": {
922+
"type": "boolean",
923+
"description": "run tests in a random order"
924+
},
909925
"test-reporter": {
910926
"oneOf": [
911927
{

doc/node.1

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

0 commit comments

Comments
 (0)