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