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('node:assert'); // 8 41const http = require('node: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('node:assert'); 99const http = require('node:http'); 100``` 101 102The test checks functionality in the `node:http` module. 103 104Most tests use the `node: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('node:assert'); 177const http = require('node: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('node: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). 254Node.js automatically crashes - and hence, the test fails - in the case of an 255`unhandledRejection` event. 256 257```js 258const common = require('../common'); 259const assert = require('node:assert'); 260const fs = require('node:fs').promises; 261 262// Wrap the `onFulfilled` handler in `common.mustCall()`. 263fs.readFile('test-file').then( 264 common.mustCall( 265 (content) => assert.strictEqual(content.toString(), 'test2'), 266 )); 267``` 268 269### Flags 270 271Some tests will require running Node.js with specific command line flags set. To 272accomplish this, add a `// Flags:` comment in the preamble of the 273test followed by the flags. For example, to allow a test to require some of the 274`internal/*` modules, add the `--expose-internals` flag. 275A test that would require `internal/freelist` could start like this: 276 277```js 278'use strict'; 279 280// Flags: --expose-internals 281 282require('../common'); 283const assert = require('node:assert'); 284const freelist = require('node:internal/freelist'); 285``` 286 287In specific scenarios it may be useful to get a hold of `primordials` or 288`internalBinding()`. You can do so using 289 290```console 291node --expose-internals -r internal/test/binding lib/fs.js 292``` 293 294This only works if you preload `node:internal/test/binding` by command line 295flag. 296 297### Assertions 298 299When writing assertions, prefer the strict versions: 300 301* `assert.strictEqual()` over `assert.equal()` 302* `assert.deepStrictEqual()` over `assert.deepEqual()` 303 304When using `assert.throws()`, if possible, provide the full error message: 305 306```js 307assert.throws( 308 () => { 309 throw new Error('Wrong value'); 310 }, 311 /^Error: Wrong value$/, // Instead of something like /Wrong value/ 312); 313``` 314 315In the case of internal errors, prefer checking only the `code` property: 316 317```js 318assert.throws( 319 () => { 320 throw new ERR_FS_FILE_TOO_LARGE(`${sizeKiB} Kb`); 321 }, 322 { code: 'ERR_FS_FILE_TOO_LARGE' }, 323 // Do not include message: /^File size ([0-9]+ Kb) is greater than 2 GiB$/ 324); 325``` 326 327### Console output 328 329Output written by tests to stdout or stderr, such as with `console.log()` or 330`console.error()`, can be useful when writing tests, as well as for debugging 331them during later maintenance. The output will be suppressed by the test runner 332(`./tools/test.py`) unless the test fails, but will always be displayed when 333running tests directly with `node`. For failing tests, the test runner will 334include the output along with the failed test assertion in the test report. 335 336Some output can help debugging by giving context to test failures. For example, 337when troubleshooting tests that timeout in CI. With no log statements, we have 338no idea where the test got hung up. 339 340There have been cases where tests fail without `console.log()`, and then pass 341when its added, so be cautious about its use, particularly in tests of the I/O 342and streaming APIs. 343 344Excessive use of console output is discouraged as it can overwhelm the display, 345including the Jenkins console and test report displays. Be particularly 346cautious of output in loops, or other contexts where output may be repeated many 347times in the case of failure. 348 349In some tests, it can be unclear whether a `console.log()` statement is required 350as part of the test (message tests, tests that check output from child 351processes, etc.), or is there as a debug aide. If there is any chance of 352confusion, use comments to make the purpose clear. 353 354### ES.Next features 355 356For performance considerations, we only use a selected subset of ES.Next 357features in JavaScript code in the `lib` directory. However, when writing 358tests, for the ease of backporting, it is encouraged to use those ES.Next 359features that can be used directly without a flag in 360[all maintained branches][]. [node.green][] lists available features 361in each release, such as: 362 363* `let` and `const` over `var` 364* Template literals over string concatenation 365* Arrow functions when appropriate 366 367## Naming test files 368 369Test files are named using kebab casing. The first component of the name is 370`test`. The second is the module or subsystem being tested. The third is usually 371the method or event name being tested. Subsequent components of the name add 372more information about what is being tested. 373 374For example, a test for the `beforeExit` event on the `process` object might be 375named `test-process-before-exit.js`. If the test specifically checked that arrow 376functions worked correctly with the `beforeExit` event, then it might be named 377`test-process-before-exit-arrow-functions.js`. 378 379## Imported tests 380 381### Web platform tests 382 383See [`test/wpt`](../../test/wpt/README.md) for more information. 384 385## C++ unit test 386 387C++ code can be tested using [Google Test][]. Most features in Node.js can be 388tested using the methods described previously in this document. But there are 389cases where these might not be enough, for example writing code for Node.js 390that will only be called when Node.js is embedded. 391 392### Adding a new test 393 394The unit test should be placed in `test/cctest` and be named with the prefix 395`test` followed by the name of unit being tested. For example, the code below 396would be placed in `test/cctest/test_env.cc`: 397 398```cpp 399#include "gtest/gtest.h" 400#include "node_test_fixture.h" 401#include "env.h" 402#include "node.h" 403#include "v8.h" 404 405static bool called_cb = false; 406static void at_exit_callback(void* arg); 407 408class EnvTest : public NodeTestFixture { }; 409 410TEST_F(EnvTest, RunAtExit) { 411 v8::HandleScope handle_scope(isolate_); 412 v8::Local<v8::Context> context = v8::Context::New(isolate_); 413 node::IsolateData* isolateData = node::CreateIsolateData(isolate_, uv_default_loop()); 414 Argv argv{"node", "-e", ";"}; 415 auto env = node::CreateEnvironment(isolateData, context, 1, *argv, 2, *argv); 416 node::AtExit(env, at_exit_callback); 417 node::RunAtExit(env); 418 EXPECT_TRUE(called_cb); 419} 420 421static void at_exit_callback(void* arg) { 422 called_cb = true; 423} 424``` 425 426Next add the test to the `sources` in the `cctest` target in node.gyp: 427 428```console 429'sources': [ 430 'test/cctest/test_env.cc', 431 ... 432], 433``` 434 435The only sources that should be included in the cctest target are 436actual test or helper source files. There might be a need to include specific 437object files that are compiled by the `node` target and this can be done by 438adding them to the `libraries` section in the cctest target. 439 440The test can be executed by running the `cctest` target: 441 442```console 443$ make cctest 444``` 445 446A filter can be applied to run single/multiple test cases: 447 448```console 449$ make cctest GTEST_FILTER=EnvironmentTest.AtExitWithArgument 450``` 451 452`cctest` can also be run directly which can be useful when debugging: 453 454```console 455$ out/Release/cctest --gtest_filter=EnvironmentTest.AtExit\* 456``` 457 458### Node.js test fixture 459 460There is a [test fixture][] named `node_test_fixture.h` which can be included by 461unit tests. The fixture takes care of setting up the Node.js environment 462and tearing it down after the tests have finished. 463 464It also contains a helper to create arguments to be passed into Node.js. It 465will depend on what is being tested if this is required or not. 466 467### Test coverage 468 469To generate a test coverage report, see the 470[Test Coverage section of the Building guide][]. 471 472Nightly coverage reports for the Node.js `main` branch are available at 473<https://coverage.nodejs.org/>. 474 475[ASCII]: https://man7.org/linux/man-pages/man7/ascii.7.html 476[Google Test]: https://github.com/google/googletest 477[Test Coverage section of the Building guide]: https://github.com/nodejs/node/blob/HEAD/BUILDING.md#running-coverage 478[`common` module]: https://github.com/nodejs/node/blob/HEAD/test/common/README.md 479[all maintained branches]: https://github.com/nodejs/lts 480[directory structure overview]: https://github.com/nodejs/node/blob/HEAD/test/README.md#test-directories 481[node.green]: https://node.green/ 482[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 483