• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# How to write a test for the Node.js project
2
3## What is a test?
4
5Most tests in Node.js core are JavaScript programs that exercise a functionality
6provided by Node.js and check that it behaves as expected. Tests should exit
7with code `0` on success. A test will fail if:
8
9* It exits by setting `process.exitCode` to a non-zero number.
10  * This is usually done by having an assertion throw an uncaught Error.
11  * Occasionally, using `process.exit(code)` may be appropriate.
12* It never exits. In this case, the test runner will terminate the test because
13  it sets a maximum time limit.
14
15Add tests when:
16
17* Adding new functionality.
18* Fixing regressions and bugs.
19* Expanding test coverage.
20
21## Test directory structure
22
23See [directory structure overview][] for outline of existing test and locations.
24When deciding on whether to expand an existing test file or create a new one,
25consider going through the files related to the subsystem.
26For example, look for `test-streams` when writing a test for `lib/streams.js`.
27
28## Test structure
29
30Let's analyze this basic test from the Node.js test suite:
31
32```js
33'use strict';                                                          // 1
34const common = require('../common');                                   // 2
35const fixtures = require('../common/fixtures');                        // 3
36
37// This test ensures that the http-parser can handle UTF-8 characters  // 5
38// in the http header.                                                 // 6
39
40const assert = require('assert');                                      // 8
41const http = require('http');                                          // 9
42
43const server = http.createServer(common.mustCall((req, res) => {       // 11
44  res.end('ok');                                                       // 12
45}));                                                                   // 13
46server.listen(0, () => {                                               // 14
47  http.get({                                                           // 15
48    port: server.address().port,                                       // 16
49    headers: { 'Test': 'Düsseldorf' }                                  // 17
50  }, common.mustCall((res) => {                                        // 18
51    assert.strictEqual(res.statusCode, 200);                           // 19
52    server.close();                                                    // 20
53  }));                                                                 // 21
54});                                                                    // 22
55// ...                                                                 // 23
56```
57
58### **Lines 1-3**
59
60```js
61'use strict';
62const common = require('../common');
63const fixtures = require('../common/fixtures');
64```
65
66The first line enables strict mode. All tests should be in strict mode unless
67the nature of the test requires that the test run without it.
68
69The second line loads the `common` module. The [`common` module][] is a helper
70module that provides useful tools for the tests. Some common functionality has
71been extracted into submodules, which are required separately like the fixtures
72module here.
73
74Even if a test uses no functions or other properties exported by `common`,
75the test should still include the `common` module before any other modules. This
76is because the `common` module includes code that will cause a test to fail if
77the test leaks variables into the global space. In situations where a test uses
78no functions or other properties exported by `common`, include it without
79assigning it to an identifier:
80
81```js
82require('../common');
83```
84
85### **Lines 5-6**
86
87```js
88// This test ensures that the http-parser can handle UTF-8 characters
89// in the http header.
90```
91
92A test should start with a comment containing a brief description of what it is
93designed to test.
94
95### **Lines 8-9**
96
97```js
98const assert = require('assert');
99const http = require('http');
100```
101
102The test checks functionality in the `http` module.
103
104Most tests use the `assert` module to confirm expectations of the test.
105
106The require statements are sorted in
107[ASCII][] order (digits, upper
108case, `_`, lower case).
109
110### **Lines 11-22**
111
112```js
113const server = http.createServer(common.mustCall((req, res) => {
114  res.end('ok');
115}));
116server.listen(0, () => {
117  http.get({
118    port: server.address().port,
119    headers: { 'Test': 'Düsseldorf' }
120  }, common.mustCall((res) => {
121    assert.strictEqual(res.statusCode, 200);
122    server.close();
123  }));
124});
125```
126
127This is the body of the test. This test is simple, it just tests that an
128HTTP server accepts `non-ASCII` characters in the headers of an incoming
129request. Interesting things to notice:
130
131* If the test doesn't depend on a specific port number, then always use 0
132  instead of an arbitrary value, as it allows tests to run in parallel safely,
133  as the operating system will assign a random port. If the test requires a
134  specific port, for example if the test checks that assigning a specific port
135  works as expected, then it is ok to assign a specific port number.
136* The use of `common.mustCall` to check that some callbacks/listeners are
137  called.
138* The HTTP server closes once all the checks have run. This way, the test can
139  exit gracefully. Remember that for a test to succeed, it must exit with a
140  status code of 0.
141
142## General recommendations
143
144### Timers
145
146Avoid timers unless the test is specifically testing timers. There are multiple
147reasons for this. Mainly, they are a source of flakiness. For a thorough
148explanation go [here](https://github.com/nodejs/testing/issues/27).
149
150In the event a test needs a timer, consider using the
151`common.platformTimeout()` method. It allows setting specific timeouts
152depending on the platform:
153
154```js
155const timer = setTimeout(fail, common.platformTimeout(4000));
156```
157
158will create a 4-second timeout on most platforms but a longer timeout on slower
159platforms.
160
161### The *common* API
162
163Make use of the helpers from the `common` module as much as possible. Please
164refer to the [common file documentation](https://github.com/nodejs/node/tree/HEAD/test/common)
165for the full details of the helpers.
166
167#### common.mustCall
168
169One interesting case is `common.mustCall`. The use of `common.mustCall` may
170avoid the use of extra variables and the corresponding assertions. Let's
171explain this with a real test from the test suite.
172
173```js
174'use strict';
175require('../common');
176const assert = require('assert');
177const http = require('http');
178
179let request = 0;
180let listening = 0;
181let response = 0;
182process.on('exit', () => {
183  assert.equal(request, 1, 'http server "request" callback was not called');
184  assert.equal(listening, 1, 'http server "listening" callback was not called');
185  assert.equal(response, 1, 'http request "response" callback was not called');
186});
187
188const server = http.createServer((req, res) => {
189  request++;
190  res.end();
191}).listen(0, () => {
192  listening++;
193  const options = {
194    agent: null,
195    port: server.address().port
196  };
197  http.get(options, (res) => {
198    response++;
199    res.resume();
200    server.close();
201  });
202});
203```
204
205This test could be greatly simplified by using `common.mustCall` like this:
206
207```js
208'use strict';
209const common = require('../common');
210const http = require('http');
211
212const server = http.createServer(common.mustCall((req, res) => {
213  res.end();
214})).listen(0, common.mustCall(() => {
215  const options = {
216    agent: null,
217    port: server.address().port
218  };
219  http.get(options, common.mustCall((res) => {
220    res.resume();
221    server.close();
222  }));
223}));
224```
225
226**Note:** Many functions invoke their callback with an `err` value as the first
227argument. It is not a good idea to simply pass `common.mustCall()` to those
228because `common.mustCall()` will ignore the error. Use `common.mustSucceed()`
229instead.
230
231#### Countdown module
232
233The common [Countdown module](https://github.com/nodejs/node/tree/HEAD/test/common#countdown-module)
234provides a simple countdown mechanism for tests that require a particular
235action to be taken after a given number of completed tasks (for instance,
236shutting down an HTTP server after a specific number of requests).
237
238```js
239const Countdown = require('../common/countdown');
240
241const countdown = new Countdown(2, () => {
242  console.log('.');
243});
244
245countdown.dec();
246countdown.dec(); // The countdown callback will be invoked now.
247```
248
249#### Testing promises
250
251When writing tests involving promises, it is generally good to wrap the
252`onFulfilled` handler, otherwise the test could successfully finish if the
253promise never resolves (pending promises do not keep the event loop alive). The
254`common` module automatically adds a handler that makes the process crash - and
255hence, the test fail - in the case of an `unhandledRejection` event. It is
256possible to disable it with `common.disableCrashOnUnhandledRejection()` if
257needed.
258
259```js
260const common = require('../common');
261const assert = require('assert');
262const fs = require('fs').promises;
263
264// Wrap the `onFulfilled` handler in `common.mustCall()`.
265fs.readFile('test-file').then(
266  common.mustCall(
267    (content) => assert.strictEqual(content.toString(), 'test2')
268  ));
269```
270
271### Flags
272
273Some tests will require running Node.js with specific command line flags set. To
274accomplish this, add a `// Flags:` comment in the preamble of the
275test followed by the flags. For example, to allow a test to require some of the
276`internal/*` modules, add the `--expose-internals` flag.
277A test that would require `internal/freelist` could start like this:
278
279```js
280'use strict';
281
282// Flags: --expose-internals
283
284require('../common');
285const assert = require('assert');
286const freelist = require('internal/freelist');
287```
288
289In specific scenarios it may be useful to get a hold of `primordials` or
290`internalBinding()`. You can do so using
291
292```console
293node --expose-internals -r internal/test/binding lib/fs.js
294```
295
296This only works if you preload `internal/test/binding` by command line flag.
297
298### Assertions
299
300When writing assertions, prefer the strict versions:
301
302* `assert.strictEqual()` over `assert.equal()`
303* `assert.deepStrictEqual()` over `assert.deepEqual()`
304
305When using `assert.throws()`, if possible, provide the full error message:
306
307```js
308assert.throws(
309  () => {
310    throw new Error('Wrong value');
311  },
312  /^Error: Wrong value$/ // Instead of something like /Wrong value/
313);
314```
315
316### Console output
317
318Output written by tests to stdout or stderr, such as with `console.log()` or
319`console.error()`, can be useful when writing tests, as well as for debugging
320them during later maintenance. The output will be suppressed by the test runner
321(`./tools/test.py`) unless the test fails, but will always be displayed when
322running tests directly with `node`. For failing tests, the test runner will
323include the output along with the failed test assertion in the test report.
324
325Some output can help debugging by giving context to test failures. For example,
326when troubleshooting tests that timeout in CI. With no log statements, we have
327no idea where the test got hung up.
328
329There have been cases where tests fail without `console.log()`, and then pass
330when its added, so be cautious about its use, particularly in tests of the I/O
331and streaming APIs.
332
333Excessive use of console output is discouraged as it can overwhelm the display,
334including the Jenkins console and test report displays. Be particularly
335cautious of output in loops, or other contexts where output may be repeated many
336times in the case of failure.
337
338In some tests, it can be unclear whether a `console.log()` statement is required
339as part of the test (message tests, tests that check output from child
340processes, etc.), or is there as a debug aide. If there is any chance of
341confusion, use comments to make the purpose clear.
342
343### ES.Next features
344
345For performance considerations, we only use a selected subset of ES.Next
346features in JavaScript code in the `lib` directory. However, when writing
347tests, for the ease of backporting, it is encouraged to use those ES.Next
348features that can be used directly without a flag in
349[all maintained branches][]. [node.green][] lists available features
350in each release, such as:
351
352* `let` and `const` over `var`
353* Template literals over string concatenation
354* Arrow functions when appropriate
355
356## Naming test files
357
358Test files are named using kebab casing. The first component of the name is
359`test`. The second is the module or subsystem being tested. The third is usually
360the method or event name being tested. Subsequent components of the name add
361more information about what is being tested.
362
363For example, a test for the `beforeExit` event on the `process` object might be
364named `test-process-before-exit.js`. If the test specifically checked that arrow
365functions worked correctly with the `beforeExit` event, then it might be named
366`test-process-before-exit-arrow-functions.js`.
367
368## Imported tests
369
370### Web platform tests
371
372See [`test/wpt`](../../test/wpt/README.md) for more information.
373
374## C++ unit test
375
376C++ code can be tested using [Google Test][]. Most features in Node.js can be
377tested using the methods described previously in this document. But there are
378cases where these might not be enough, for example writing code for Node.js
379that will only be called when Node.js is embedded.
380
381### Adding a new test
382
383The unit test should be placed in `test/cctest` and be named with the prefix
384`test` followed by the name of unit being tested. For example, the code below
385would be placed in `test/cctest/test_env.cc`:
386
387```cpp
388#include "gtest/gtest.h"
389#include "node_test_fixture.h"
390#include "env.h"
391#include "node.h"
392#include "v8.h"
393
394static bool called_cb = false;
395static void at_exit_callback(void* arg);
396
397class EnvTest : public NodeTestFixture { };
398
399TEST_F(EnvTest, RunAtExit) {
400  v8::HandleScope handle_scope(isolate_);
401  v8::Local<v8::Context> context = v8::Context::New(isolate_);
402  node::IsolateData* isolateData = node::CreateIsolateData(isolate_, uv_default_loop());
403  Argv argv{"node", "-e", ";"};
404  auto env = node::CreateEnvironment(isolateData, context, 1, *argv, 2, *argv);
405  node::AtExit(env, at_exit_callback);
406  node::RunAtExit(env);
407  EXPECT_TRUE(called_cb);
408}
409
410static void at_exit_callback(void* arg) {
411  called_cb = true;
412}
413```
414
415Next add the test to the `sources` in the `cctest` target in node.gyp:
416
417```console
418'sources': [
419  'test/cctest/test_env.cc',
420  ...
421],
422```
423
424The only sources that should be included in the cctest target are
425actual test or helper source files. There might be a need to include specific
426object files that are compiled by the `node` target and this can be done by
427adding them to the `libraries` section in the cctest target.
428
429The test can be executed by running the `cctest` target:
430
431```console
432$ make cctest
433```
434
435A filter can be applied to run single/multiple test cases:
436
437```console
438$ make cctest GTEST_FILTER=EnvironmentTest.AtExitWithArgument
439```
440
441`cctest` can also be run directly which can be useful when debugging:
442
443```console
444$ out/Release/cctest --gtest_filter=EnvironmentTest.AtExit\*
445```
446
447### Node.js test fixture
448There is a [test fixture][] named `node_test_fixture.h` which can be included by
449unit tests. The fixture takes care of setting up the Node.js environment
450and tearing it down after the tests have finished.
451
452It also contains a helper to create arguments to be passed into Node.js. It
453will depend on what is being tested if this is required or not.
454
455### Test coverage
456
457To generate a test coverage report, see the
458[Test Coverage section of the Building guide][].
459
460Nightly coverage reports for the Node.js master branch are available at
461<https://coverage.nodejs.org/>.
462
463[ASCII]: https://man7.org/linux/man-pages/man7/ascii.7.html
464[Google Test]: https://github.com/google/googletest
465[Test Coverage section of the Building guide]: https://github.com/nodejs/node/blob/HEAD/BUILDING.md#running-coverage
466[`common` module]: https://github.com/nodejs/node/blob/HEAD/test/common/README.md
467[all maintained branches]: https://github.com/nodejs/lts
468[directory structure overview]: https://github.com/nodejs/node/blob/HEAD/test/README.md#test-directories
469[node.green]: https://node.green/
470[test fixture]: https://github.com/google/googletest/blob/HEAD/docs/primer.md#test-fixtures-using-the-same-data-configuration-for-multiple-tests-same-data-multiple-tests
471