mirror of https://github.com/lukechilds/node.git
Browse Source
PR-URL: https://github.com/nodejs/node/pull/6984 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Yorkie Liu <yorkiefixer@gmail.com>v7.x
2 changed files with 189 additions and 2 deletions
@ -0,0 +1,186 @@ |
|||
# How to write a test for the Node.js project |
|||
|
|||
## What is a test? |
|||
|
|||
A test must be a node script that exercises a specific functionality provided |
|||
by node and checks that it behaves as expected. It should return 0 on success, |
|||
otherwise it will fail. A test will fail if: |
|||
|
|||
- It exits by calling `process.exit(code)` where `code != 0` |
|||
- It exits due to an uncaught exception. |
|||
- It never exits. In this case, the test runner will terminate the test because |
|||
it sets a maximum time limit. |
|||
|
|||
Tests can be added for multiple reasons: |
|||
|
|||
- When adding new functionality. |
|||
- When fixing regressions and bugs. |
|||
- When expanding test coverage. |
|||
|
|||
|
|||
## Test structure |
|||
|
|||
Let's analyze this very basic test from the Node.js test suite: |
|||
|
|||
```javascript |
|||
1 'use strict'; |
|||
2 const common = require('../common'); |
|||
3 const http = require('http'); |
|||
4 const assert = require('assert'); |
|||
5 |
|||
6 const server = http.createServer(common.mustCall((req, res) => { |
|||
7 res.end('ok'); |
|||
8 })); |
|||
9 server.listen(common.PORT, () => { |
|||
10 http.get({ |
|||
11 port: common.PORT, |
|||
12 headers: {'Test': 'Düsseldorf'} |
|||
13 }, common.mustCall((res) => { |
|||
14 assert.equal(res.statusCode, 200); |
|||
15 server.close(); |
|||
16 })); |
|||
17 }); |
|||
``` |
|||
|
|||
**Lines 1-2** |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
const common = require('../common'); |
|||
``` |
|||
|
|||
These two lines are mandatory and should be included on every test. |
|||
The `common` module is a helper module that provides useful tools for the tests. |
|||
If for some reason, no functionality from `common` is used, it should still be |
|||
included like this: |
|||
|
|||
```javascript |
|||
require('../common'); |
|||
``` |
|||
|
|||
Why? It checks for leaks of globals. |
|||
|
|||
**Lines 3-4** |
|||
|
|||
```javascript |
|||
const http = require('http'); |
|||
const assert = require('assert'); |
|||
``` |
|||
|
|||
These modules are required for the test to run. Except for special cases, these |
|||
modules should only include core modules. |
|||
The `assert` module is used by most of the tests to check that the assumptions |
|||
for the test are met. |
|||
|
|||
**Lines 6-17** |
|||
|
|||
This is the body of the test. This test is quite simple, it just tests that an |
|||
HTTP server accepts `non-ASCII` characters in the headers of an incoming |
|||
request. Interesting things to notice: |
|||
|
|||
- The use of `common.PORT` as the listening port. Always use `common.PORT` |
|||
instead of using an arbitrary value, as it allows to run tests in parallel |
|||
safely, as they are not trying to reuse the same port another test is already |
|||
using. |
|||
- The use of `common.mustCall` to check that some callbacks/listeners are |
|||
called. |
|||
- The HTTP server is closed once all the checks have run. This way, the test can |
|||
exit gracefully. Remember that for a test to succeed, it must exit with a |
|||
status code of 0. |
|||
|
|||
## General recommendations |
|||
|
|||
### Timers |
|||
|
|||
The use of timers is discouraged, unless timers are being tested. There are |
|||
multiple reasons for this. Mainly, they are a source of flakiness. For a thorough |
|||
explanation go [here](https://github.com/nodejs/testing/issues/27). |
|||
|
|||
In the event a timer is needed, it's recommended using the |
|||
`common.platformTimeout()` method, that allows setting specific timeouts |
|||
depending on the platform. For example: |
|||
|
|||
```javascript |
|||
const timer = setTimeout(fail, common.platformTimeout(4000)); |
|||
``` |
|||
|
|||
will create a 4-seconds timeout, except for some platforms where the delay will |
|||
be multiplied for some factor. |
|||
|
|||
### The *common* API |
|||
|
|||
Make use of the helpers from the `common` module as much as possible. |
|||
|
|||
One interesting case is `common.mustCall`. The use of `common.mustCall` may |
|||
avoid the use of extra variables and the corresponding assertions. Let's explain |
|||
this with a real test from the test suite. |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var http = require('http'); |
|||
|
|||
var request = 0; |
|||
var response = 0; |
|||
process.on('exit', function() { |
|||
assert.equal(request, 1, 'http server "request" callback was not called'); |
|||
assert.equal(response, 1, 'http request "response" callback was not called'); |
|||
}); |
|||
|
|||
var server = http.createServer(function(req, res) { |
|||
request++; |
|||
res.end(); |
|||
}).listen(common.PORT, function() { |
|||
var options = { |
|||
agent: null, |
|||
port: this.address().port |
|||
}; |
|||
http.get(options, function(res) { |
|||
response++; |
|||
res.resume(); |
|||
server.close(); |
|||
}); |
|||
}); |
|||
``` |
|||
|
|||
This test could be greatly simplified by using `common.mustCall` like this: |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var http = require('http'); |
|||
|
|||
var server = http.createServer(common.mustCall(function(req, res) { |
|||
res.end(); |
|||
})).listen(common.PORT, function() { |
|||
var options = { |
|||
agent: null, |
|||
port: this.address().port |
|||
}; |
|||
http.get(options, common.mustCall(function(res) { |
|||
res.resume(); |
|||
server.close(); |
|||
})); |
|||
}); |
|||
|
|||
``` |
|||
|
|||
### Flags |
|||
|
|||
Some tests will require running Node.js with specific command line flags set. To |
|||
accomplish this, a `// Flags: ` comment should be added in the preamble of the |
|||
test followed by the flags. For example, to allow a test to require some of the |
|||
`internal/*` modules, the `--expose-internals` flag should be added. |
|||
A test that would require `internal/freelist` could start like this: |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
|
|||
// Flags: --expose-internals |
|||
|
|||
require('../common'); |
|||
const assert = require('assert'); |
|||
const freelist = require('internal/freelist'); |
|||
``` |
Loading…
Reference in new issue