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 & 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 112This is the body of the test. This test is simple, it just tests that an 113HTTP server accepts `non-ASCII` characters in the headers of an incoming 114request. Interesting things to notice: 115 116* If the test doesn't depend on a specific port number, then always use 0 117 instead of an arbitrary value, as it allows tests to run in parallel safely, 118 as the operating system will assign a random port. If the test requires a 119 specific port, for example if the test checks that assigning a specific port 120 works as expected, then it is ok to assign a specific port number. 121* The use of `common.mustCall` to check that some callbacks/listeners are 122 called. 123* The HTTP server closes once all the checks have run. This way, the test can 124 exit gracefully. Remember that for a test to succeed, it must exit with a 125 status code of 0. 126 127## General recommendations 128 129### Timers 130 131Avoid timers unless the test is specifically testing timers. There are multiple 132reasons for this. Mainly, they are a source of flakiness. For a thorough 133explanation go [here](https://github.com/nodejs/testing/issues/27). 134 135In the event a test needs a timer, consider using the 136`common.platformTimeout()` method. It allows setting specific timeouts 137depending on the platform: 138 139```js 140const timer = setTimeout(fail, common.platformTimeout(4000)); 141``` 142 143will create a 4-second timeout on most platforms but a longer timeout on slower 144platforms. 145 146### The *common* API 147 148Make use of the helpers from the `common` module as much as possible. Please 149refer to the [common file documentation](https://github.com/nodejs/node/tree/master/test/common) 150for the full details of the helpers. 151 152#### common.mustCall 153 154One interesting case is `common.mustCall`. The use of `common.mustCall` may 155avoid the use of extra variables and the corresponding assertions. Let's 156explain this with a real test from the test suite. 157 158```js 159'use strict'; 160require('../common'); 161const assert = require('assert'); 162const http = require('http'); 163 164let request = 0; 165let listening = 0; 166let response = 0; 167process.on('exit', () => { 168 assert.equal(request, 1, 'http server "request" callback was not called'); 169 assert.equal(listening, 1, 'http server "listening" callback was not called'); 170 assert.equal(response, 1, 'http request "response" callback was not called'); 171}); 172 173const server = http.createServer((req, res) => { 174 request++; 175 res.end(); 176}).listen(0, () => { 177 listening++; 178 const options = { 179 agent: null, 180 port: server.address().port 181 }; 182 http.get(options, (res) => { 183 response++; 184 res.resume(); 185 server.close(); 186 }); 187}); 188``` 189 190This test could be greatly simplified by using `common.mustCall` like this: 191 192```js 193'use strict'; 194const common = require('../common'); 195const http = require('http'); 196 197const server = http.createServer(common.mustCall((req, res) => { 198 res.end(); 199})).listen(0, common.mustCall(() => { 200 const options = { 201 agent: null, 202 port: server.address().port 203 }; 204 http.get(options, common.mustCall((res) => { 205 res.resume(); 206 server.close(); 207 })); 208})); 209 210``` 211 212#### Countdown Module 213 214The common [Countdown module](https://github.com/nodejs/node/tree/master/test/common#countdown-module) 215provides a simple countdown mechanism for tests that require a particular 216action to be taken after a given number of completed tasks (for instance, 217shutting down an HTTP server after a specific number of requests). 218 219```js 220const Countdown = require('../common/countdown'); 221 222const countdown = new Countdown(2, () => { 223 console.log('.'); 224}); 225 226countdown.dec(); 227countdown.dec(); // The countdown callback will be invoked now. 228``` 229 230#### Testing promises 231 232When writing tests involving promises, it is generally good to wrap the 233`onFulfilled` handler, otherwise the test could successfully finish if the 234promise never resolves (pending promises do not keep the event loop alive). The 235`common` module automatically adds a handler that makes the process crash - and 236hence, the test fail - in the case of an `unhandledRejection` event. It is 237possible to disable it with `common.disableCrashOnUnhandledRejection()` if 238needed. 239 240```js 241const common = require('../common'); 242const assert = require('assert'); 243const fs = require('fs').promises; 244 245// Wrap the `onFulfilled` handler in `common.mustCall()`. 246fs.readFile('test-file').then( 247 common.mustCall( 248 (content) => assert.strictEqual(content.toString(), 'test2') 249 )); 250``` 251 252### Flags 253 254Some tests will require running Node.js with specific command line flags set. To 255accomplish this, add a `// Flags:` comment in the preamble of the 256test followed by the flags. For example, to allow a test to require some of the 257`internal/*` modules, add the `--expose-internals` flag. 258A test that would require `internal/freelist` could start like this: 259 260```js 261'use strict'; 262 263// Flags: --expose-internals 264 265require('../common'); 266const assert = require('assert'); 267const freelist = require('internal/freelist'); 268``` 269 270### Assertions 271 272When writing assertions, prefer the strict versions: 273 274* `assert.strictEqual()` over `assert.equal()` 275* `assert.deepStrictEqual()` over `assert.deepEqual()` 276 277When using `assert.throws()`, if possible, provide the full error message: 278 279```js 280assert.throws( 281 () => { 282 throw new Error('Wrong value'); 283 }, 284 /^Error: Wrong value$/ // Instead of something like /Wrong value/ 285); 286``` 287 288### Console output 289 290Output written by tests to stdout or stderr, such as with `console.log()` or 291`console.error()`, can be useful when writing tests, as well as for debugging 292them during later maintenance. The output will be suppressed by the test runner 293(`./tools/test.py`) unless the test fails, but will always be displayed when 294running tests directly with `node`. For failing tests, the test runner will 295include the output along with the failed test assertion in the test report. 296 297Some output can help debugging by giving context to test failures. For example, 298when troubleshooting tests that timeout in CI. With no log statements, we have 299no idea where the test got hung up. 300 301There have been cases where tests fail without `console.log()`, and then pass 302when its added, so be cautious about its use, particularly in tests of the I/O 303and streaming APIs. 304 305Excessive use of console output is discouraged as it can overwhelm the display, 306including the Jenkins console and test report displays. Be particularly 307cautious of output in loops, or other contexts where output may be repeated many 308times in the case of failure. 309 310In some tests, it can be unclear whether a `console.log()` statement is required 311as part of the test (message tests, tests that check output from child 312processes, etc.), or is there as a debug aide. If there is any chance of 313confusion, use comments to make the purpose clear. 314 315### ES.Next features 316 317For performance considerations, we only use a selected subset of ES.Next 318features in JavaScript code in the `lib` directory. However, when writing 319tests, for the ease of backporting, it is encouraged to use those ES.Next 320features that can be used directly without a flag in 321[all maintained branches][]. [node.green][] lists available features 322in each release, such as: 323 324* `let` and `const` over `var` 325* Template literals over string concatenation 326* Arrow functions when appropriate 327 328## Naming Test Files 329 330Test files are named using kebab casing. The first component of the name is 331`test`. The second is the module or subsystem being tested. The third is usually 332the method or event name being tested. Subsequent components of the name add 333more information about what is being tested. 334 335For example, a test for the `beforeExit` event on the `process` object might be 336named `test-process-before-exit.js`. If the test specifically checked that arrow 337functions worked correctly with the `beforeExit` event, then it might be named 338`test-process-before-exit-arrow-functions.js`. 339 340## Imported Tests 341 342### Web Platform Tests 343 344See [`test/wpt`](../../test/wpt/README.md) for more information. 345 346## C++ Unit test 347 348C++ code can be tested using [Google Test][]. Most features in Node.js can be 349tested using the methods described previously in this document. But there are 350cases where these might not be enough, for example writing code for Node.js 351that will only be called when Node.js is embedded. 352 353### Adding a new test 354 355The unit test should be placed in `test/cctest` and be named with the prefix 356`test` followed by the name of unit being tested. For example, the code below 357would be placed in `test/cctest/test_env.cc`: 358 359```cpp 360#include "gtest/gtest.h" 361#include "node_test_fixture.h" 362#include "env.h" 363#include "node.h" 364#include "v8.h" 365 366static bool called_cb = false; 367static void at_exit_callback(void* arg); 368 369class EnvTest : public NodeTestFixture { }; 370 371TEST_F(EnvTest, RunAtExit) { 372 v8::HandleScope handle_scope(isolate_); 373 v8::Local<v8::Context> context = v8::Context::New(isolate_); 374 node::IsolateData* isolateData = node::CreateIsolateData(isolate_, uv_default_loop()); 375 Argv argv{"node", "-e", ";"}; 376 auto env = node::CreateEnvironment(isolateData, context, 1, *argv, 2, *argv); 377 node::AtExit(env, at_exit_callback); 378 node::RunAtExit(env); 379 EXPECT_TRUE(called_cb); 380} 381 382static void at_exit_callback(void* arg) { 383 called_cb = true; 384} 385``` 386 387Next add the test to the `sources` in the `cctest` target in node.gyp: 388 389```console 390'sources': [ 391 'test/cctest/test_env.cc', 392 ... 393], 394``` 395 396The only sources that should be included in the cctest target are 397actual test or helper source files. There might be a need to include specific 398object files that are compiled by the `node` target and this can be done by 399adding them to the `libraries` section in the cctest target. 400 401The test can be executed by running the `cctest` target: 402 403```console 404$ make cctest 405``` 406 407A filter can be applied to run single/multiple test cases: 408 409```console 410$ make cctest GTEST_FILTER=EnvironmentTest.AtExitWithArgument 411``` 412 413`cctest` can also be run directly which can be useful when debugging: 414 415```console 416$ out/Release/cctest --gtest_filter=EnvironmentTest.AtExit* 417``` 418 419### Node.js test fixture 420There is a [test fixture][] named `node_test_fixture.h` which can be included by 421unit tests. The fixture takes care of setting up the Node.js environment 422and tearing it down after the tests have finished. 423 424It also contains a helper to create arguments to be passed into Node.js. It 425will depend on what is being tested if this is required or not. 426 427### Test Coverage 428 429To generate a test coverage report, see the 430[Test Coverage section of the Building guide][]. 431 432Nightly coverage reports for the Node.js master branch are available at 433<https://coverage.nodejs.org/>. 434 435[ASCII]: https://man7.org/linux/man-pages/man7/ascii.7.html 436[Google Test]: https://github.com/google/googletest 437[Test Coverage section of the Building guide]: https://github.com/nodejs/node/blob/master/BUILDING.md#running-coverage 438[`common` module]: https://github.com/nodejs/node/blob/master/test/common/README.md 439[all maintained branches]: https://github.com/nodejs/lts 440[directory structure overview]: https://github.com/nodejs/node/blob/master/test/README.md#test-directories 441[node.green]: https://node.green/ 442[test fixture]: https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#test-fixtures-using-the-same-data-configuration-for-multiple-tests 443