Skip to content

Commit 8c6dffc

Browse files
cjihrigtargos
authored andcommitted
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. PR-URL: #53169 Fixes: #48260 Reviewed-By: Moshe Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]>
1 parent 048478d commit 8c6dffc

File tree

14 files changed

+824
-4
lines changed

14 files changed

+824
-4
lines changed

doc/api/cli.md

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

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

9941004
<!-- YAML
@@ -2145,6 +2155,18 @@ added:
21452155
A number of milliseconds the test execution will fail after. If unspecified,
21462156
subtests inherit this value from their parent. The default value is `Infinity`.
21472157

2158+
### `--test-update-snapshots`
2159+
2160+
<!-- YAML
2161+
added: REPLACEME
2162+
-->
2163+
2164+
> Stability: 1.0 - Early development
2165+
2166+
Regenerates the snapshot file used by the test runner for [snapshot testing][].
2167+
Node.js must be started with the `--experimental-test-snapshots` flag in order
2168+
to use this functionality.
2169+
21482170
### `--throw-deprecation`
21492171

21502172
<!-- YAML
@@ -3323,6 +3345,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
33233345
[security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
33243346
[semi-space]: https://www.memorymanagement.org/glossary/s.html#semi.space
33253347
[single executable application]: single-executable-applications.md
3348+
[snapshot testing]: test.md#snapshot-testing
33263349
[test reporters]: test.md#test-reporters
33273350
[timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
33283351
[tracking issue for user-land snapshots]: https://github.com/nodejs/node/issues/44014

doc/api/test.md

Lines changed: 140 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. Best
931+
practice is for snapshot files to be checked into source control along with your
932+
test files. In order to enable snapshot testing, Node.js must be started with
933+
the [`--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 file. By default, the snapshot file has the same name
938+
as `process.argv[1]` with a `.snapshot` file extension. This behavior can be
939+
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
@@ -1612,6 +1667,54 @@ describe('tests', async () => {
16121667
});
16131668
```
16141669

1670+
## `snapshot`
1671+
1672+
<!-- YAML
1673+
added: REPLACEME
1674+
-->
1675+
1676+
> Stability: 1.0 - Early development
1677+
1678+
An object whose methods are used to configure default snapshot settings in the
1679+
current process. It is possible to apply the same configuration to all files by
1680+
placing common configuration code in a module preloaded with `--require` or
1681+
`--import`.
1682+
1683+
### `snapshot.setDefaultSnapshotSerializers(serializers)`
1684+
1685+
<!-- YAML
1686+
added: REPLACEME
1687+
-->
1688+
1689+
> Stability: 1.0 - Early development
1690+
1691+
* `serializers` {Array} An array of synchronous functions used as the default
1692+
serializers for snapshot tests.
1693+
1694+
This function is used to customize the default serialization mechanism used by
1695+
the test runner. By default, the test runner performs serialization by calling
1696+
`JSON.stringify(value, null, 2)` on the provided value. `JSON.stringify()` does
1697+
have limitations regarding circular structures and supported data types. If a
1698+
more robust serialization mechanism is required, this function should be used.
1699+
1700+
### `snapshot.setResolveSnapshotPath(fn)`
1701+
1702+
<!-- YAML
1703+
added: REPLACEME
1704+
-->
1705+
1706+
> Stability: 1.0 - Early development
1707+
1708+
* `fn` {Function} A function used to compute the location of the snapshot file.
1709+
The function receives the path of the test file as its only argument. If the
1710+
`process.argv[1]` is not associated with a file (for example in the REPL),
1711+
the input is undefined. `fn()` must return a string specifying the location of
1712+
the snapshot file.
1713+
1714+
This function is used to customize the location of the snapshot file used for
1715+
snapshot testing. By default, the snapshot filename is the same as the entry
1716+
point filename with a `.snapshot` file extension.
1717+
16151718
## Class: `MockFunctionContext`
16161719

16171720
<!-- YAML
@@ -3032,6 +3135,41 @@ test('test', (t) => {
30323135
});
30333136
```
30343137

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

30373175
<!-- YAML
@@ -3310,13 +3448,15 @@ Can be used to abort test subtasks when the test has been aborted.
33103448
[TAP]: https://testanything.org/
33113449
[TTY]: tty.md
33123450
[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage
3451+
[`--experimental-test-snapshots`]: cli.md#--experimental-test-snapshots
33133452
[`--import`]: cli.md#--importmodule
33143453
[`--test-concurrency`]: cli.md#--test-concurrency
33153454
[`--test-name-pattern`]: cli.md#--test-name-pattern
33163455
[`--test-only`]: cli.md#--test-only
33173456
[`--test-reporter-destination`]: cli.md#--test-reporter-destination
33183457
[`--test-reporter`]: cli.md#--test-reporter
33193458
[`--test-skip-pattern`]: cli.md#--test-skip-pattern
3459+
[`--test-update-snapshots`]: cli.md#--test-update-snapshots
33203460
[`--test`]: cli.md#--test
33213461
[`MockFunctionContext`]: #class-mockfunctioncontext
33223462
[`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
.
@@ -460,6 +463,9 @@ whose name matches the provided pattern.
460463
.It Fl -test-timeout
461464
A number of milliseconds the test execution will fail after.
462465
.
466+
.It Fl -test-update-snapshots
467+
Regenerates the snapshot file used by the test runner for snapshot testing.
468+
.
463469
.It Fl -throw-deprecation
464470
Throw errors for deprecations.
465471
.

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)