Skip to content

Commit 3c76dbb

Browse files
committed
test_runner: add snapshot testing
This commit adds a t.assert.snapshot() method that implements snapshot testing. Serialization uses JSON.stringify() by default, but users can configure the serialization to meet their needs. Fixes: #48260
1 parent d37cbab commit 3c76dbb

File tree

14 files changed

+792
-4
lines changed

14 files changed

+792
-4
lines changed

doc/api/cli.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,16 @@ added: REPLACEME
991991
992992
Enable module mocking in the test runner.
993993

994+
### `--experimental-test-snapshots`
995+
996+
<!-- YAML
997+
added: REPLACEME
998+
-->
999+
1000+
> Stability: 1.0 - Early development
1001+
1002+
Enable [snapshot testing][] in the test runner.
1003+
9941004
### `--experimental-vm-modules`
9951005

9961006
<!-- YAML
@@ -2127,6 +2137,18 @@ added:
21272137
A number of milliseconds the test execution will fail after. If unspecified,
21282138
subtests inherit this value from their parent. The default value is `Infinity`.
21292139

2140+
### `--test-update-snapshots`
2141+
2142+
<!-- YAML
2143+
added: REPLACEME
2144+
-->
2145+
2146+
> Stability: 1.0 - Early development
2147+
2148+
Regenerates the snapshot file used by the test runner for [snapshot testing][].
2149+
Node.js must be started with the `--experimental-test-snapshots` flag in order
2150+
to use this functionality.
2151+
21302152
### `--throw-deprecation`
21312153

21322154
<!-- YAML
@@ -3267,6 +3289,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
32673289
[security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
32683290
[semi-space]: https://www.memorymanagement.org/glossary/s.html#semi.space
32693291
[single executable application]: single-executable-applications.md
3292+
[snapshot testing]: test.md#snapshot-testing
32703293
[test reporters]: test.md#test-reporters
32713294
[timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
32723295
[tracking issue for user-land snapshots]: https://github.com/nodejs/node/issues/44014

doc/api/test.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,61 @@ test('runs timers as setTime passes ticks', (context) => {
920920
});
921921
```
922922

923+
## Snapshot testing
924+
925+
> Stability: 1.0 - Early development
926+
927+
Snapshot tests allow arbitrary values to be serialized into string values and
928+
compared against a set of known good values. The known good values are known as
929+
snapshots, and are stored in a snapshot file. Snapshot files are managed by the
930+
test runner, but are designed to be human readable to aid in debugging. Snapshot
931+
files should be checked into source control along with your test files. In order
932+
to enable snapshot testing, Node.js must be started with the
933+
[`--experimental-test-snapshots`][] command-line flag.
934+
935+
Snapshot files are generated by starting Node.js with the
936+
[`--test-update-snapshots`][] command-line flag. A separate snapshot file is
937+
generated for each test entry point. By default, the snapshot file has the same
938+
name as the entry point file with a `.snapshot` file extension. This behavior
939+
can be configured using the `snapshot.setResolveSnapshotPath()` function. Each
940+
snapshot assertion corresponds to an export in the snapshot file.
941+
942+
An example snapshot test is shown below. The first time this test is executed,
943+
it will fail because the corresponding snapshot file does not exist.
944+
945+
```js
946+
// test.js
947+
suite('suite of snapshot tests', () => {
948+
test('snapshot test', (t) => {
949+
t.assert.snapshot({ value1: 1, value2: 2 });
950+
t.assert.snapshot(5);
951+
});
952+
});
953+
```
954+
955+
Generate the snapshot file by running the test file with
956+
`--test-update-snapshots`. The test should pass, and a file named
957+
`test.js.snapshot` is created in the same directory as the test file. The
958+
contents of the snapshot file are shown below. Each snapshot is identified by
959+
the full name of test and a counter to differentiate between snapshots in the
960+
same test.
961+
962+
```js
963+
exports[`suite of snapshot tests > snapshot test 1`] = `
964+
{
965+
"value1": 1,
966+
"value2": 2
967+
}
968+
`;
969+
970+
exports[`suite of snapshot tests > snapshot test 2`] = `
971+
5
972+
`;
973+
```
974+
975+
Once the snapshot file is created, run the tests again without the
976+
`--test-update-snapshots` flag. The tests should pass now.
977+
923978
## Test reporters
924979

925980
<!-- YAML
@@ -1620,6 +1675,49 @@ describe('tests', async () => {
16201675
});
16211676
```
16221677

1678+
## `snapshot`
1679+
1680+
<!-- YAML
1681+
added: REPLACEME
1682+
-->
1683+
1684+
> Stability: 1.0 - Early development
1685+
1686+
An object whose methods are used to configure snapshot testing settings.
1687+
1688+
### `snapshot.setDefaultSerializers(serializers)`
1689+
1690+
<!-- YAML
1691+
added: REPLACEME
1692+
-->
1693+
1694+
> Stability: 1.0 - Early development
1695+
1696+
* `serializers` {Array} An array of synchronous functions used as the default
1697+
serializers for snapshot tests.
1698+
1699+
This function is used to customize the default serialization mechanism used by
1700+
the test runner. By default, the test runner performs serialization by calling
1701+
`JSON.stringify(value, null, 2)` on the provided value.
1702+
1703+
### `snapshot.setResolveSnapshotPath(fn)`
1704+
1705+
<!-- YAML
1706+
added: REPLACEME
1707+
-->
1708+
1709+
> Stability: 1.0 - Early development
1710+
1711+
* `fn` {Function} A function used to compute the location of the snapshot file.
1712+
The function receives the path of the entry point file as its only argument.
1713+
If the entry point is not associated with a file (for example in the REPL),
1714+
the input is undefined. `fn()` must return a string specifying the location of
1715+
the snapshot file.
1716+
1717+
This function is used to customize the location of the snapshot file used for
1718+
snapshot testing. By default, the snapshot filename is the same as the entry
1719+
point filename with a `.snapshot` file extension.
1720+
16231721
## Class: `MockFunctionContext`
16241722

16251723
<!-- YAML
@@ -3048,6 +3146,41 @@ test('test', (t) => {
30483146
});
30493147
```
30503148

3149+
#### `context.assert.snapshot(actual[, options])`
3150+
3151+
<!-- YAML
3152+
added: REPLACEME
3153+
-->
3154+
3155+
> Stability: 1.0 - Early development
3156+
3157+
* `actual` {any} A value to serialize to a string. If Node.js was started with
3158+
the [`--test-update-snapshots`][] flag, the serialized value is written to
3159+
the snapshot file. Otherwise, the serialized value is compared to the
3160+
corresponding value in the existing snapshot file.
3161+
* `options` {Object} Optional configuration options. The following properties
3162+
are supported:
3163+
* `serializers` {Array} An array of synchronous functions used to serialize
3164+
`actual` into a string. `actual` is passed as the only argument to the first
3165+
serializer function. The return value of each serializer is passed as input
3166+
to the next serializer. Once all serializers have run, the resulting value
3167+
is coerced to a string. **Default:** If no serializers are provided, the
3168+
test runner's default serializers are used.
3169+
3170+
This function implements assertions for snapshot testing.
3171+
3172+
```js
3173+
test('snapshot test with default serialization', (t) => {
3174+
t.assert.snapshot({ value1: 1, value2: 2 });
3175+
});
3176+
3177+
test('snapshot test with custom serialization', (t) => {
3178+
t.assert.snapshot({ value3: 3, value4: 4 }, {
3179+
serializers: [(value) => JSON.stringify(value)]
3180+
});
3181+
});
3182+
```
3183+
30513184
### `context.diagnostic(message)`
30523185

30533186
<!-- YAML
@@ -3326,13 +3459,15 @@ Can be used to abort test subtasks when the test has been aborted.
33263459
[TAP]: https://testanything.org/
33273460
[TTY]: tty.md
33283461
[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage
3462+
[`--experimental-test-snapshots`]: cli.md#--experimental-test-snapshots
33293463
[`--import`]: cli.md#--importmodule
33303464
[`--test-concurrency`]: cli.md#--test-concurrency
33313465
[`--test-name-pattern`]: cli.md#--test-name-pattern
33323466
[`--test-only`]: cli.md#--test-only
33333467
[`--test-reporter-destination`]: cli.md#--test-reporter-destination
33343468
[`--test-reporter`]: cli.md#--test-reporter
33353469
[`--test-skip-pattern`]: cli.md#--test-skip-pattern
3470+
[`--test-update-snapshots`]: cli.md#--test-update-snapshots
33363471
[`--test`]: cli.md#--test
33373472
[`MockFunctionContext`]: #class-mockfunctioncontext
33383473
[`MockTimers`]: #class-mocktimers

doc/node.1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ Enable code coverage in the test runner.
188188
.It Fl -experimental-test-module-mocks
189189
Enable module mocking in the test runner.
190190
.
191+
.It Fl -experimental-test-snapshots
192+
Enable snapshot testing in the test runner.
193+
.
191194
.It Fl -experimental-eventsource
192195
Enable experimental support for the EventSource Web API.
193196
.
@@ -451,6 +454,9 @@ whose name matches the provided pattern.
451454
.It Fl -test-timeout
452455
A number of milliseconds the test execution will fail after.
453456
.
457+
.It Fl -test-update-snapshots
458+
Regenerates the snapshot file used by the test runner for snapshot testing.
459+
.
454460
.It Fl -throw-deprecation
455461
Throw errors for deprecations.
456462
.

lib/internal/test_runner/harness.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ function setup(root) {
210210
counters: null,
211211
shouldColorizeTestFiles: false,
212212
teardown: exitHandler,
213+
snapshotManager: null,
213214
};
214215
root.harness.resetCounters();
215216
root.startTime = hrtime();

0 commit comments

Comments
 (0)