Skip to content

Commit 35e7c6a

Browse files
cjihrigeliphazbouye
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: nodejs#53169 Fixes: nodejs#48260 Reviewed-By: Moshe Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]>
1 parent d744dc9 commit 35e7c6a

File tree

14 files changed

+824
-4
lines changed

14 files changed

+824
-4
lines changed

doc/api/cli.md

+23
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
@@ -2129,6 +2139,18 @@ added:
21292139
A number of milliseconds the test execution will fail after. If unspecified,
21302140
subtests inherit this value from their parent. The default value is `Infinity`.
21312141

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

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

doc/api/test.md

+140
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,61 @@ test('runs timers as setTime passes ticks', (context) => {
923923
});
924924
```
925925

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

928983
<!-- YAML
@@ -1625,6 +1680,54 @@ describe('tests', async () => {
16251680
});
16261681
```
16271682

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

16301733
<!-- YAML
@@ -3045,6 +3148,41 @@ test('test', (t) => {
30453148
});
30463149
```
30473150

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

30503188
<!-- YAML
@@ -3323,13 +3461,15 @@ Can be used to abort test subtasks when the test has been aborted.
33233461
[TAP]: https://testanything.org/
33243462
[TTY]: tty.md
33253463
[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage
3464+
[`--experimental-test-snapshots`]: cli.md#--experimental-test-snapshots
33263465
[`--import`]: cli.md#--importmodule
33273466
[`--test-concurrency`]: cli.md#--test-concurrency
33283467
[`--test-name-pattern`]: cli.md#--test-name-pattern
33293468
[`--test-only`]: cli.md#--test-only
33303469
[`--test-reporter-destination`]: cli.md#--test-reporter-destination
33313470
[`--test-reporter`]: cli.md#--test-reporter
33323471
[`--test-skip-pattern`]: cli.md#--test-skip-pattern
3472+
[`--test-update-snapshots`]: cli.md#--test-update-snapshots
33333473
[`--test`]: cli.md#--test
33343474
[`MockFunctionContext`]: #class-mockfunctioncontext
33353475
[`MockTimers`]: #class-mocktimers

doc/node.1

+6
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

+1
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)