|
|
@@ -0,0 +1,234 @@ |
|
|
# Node.js 18 Test Runner |
|
|
|
|
|
Node.js v18 introduces test runner support. This currently experimental feature |
|
|
gives developers the benefits of a structured test harnesses for their package |
|
|
without having to install a third party test framework, like Mocha or Jest, as a |
|
|
dependency. Using the test runner produces [TAP] output. |
|
|
|
|
|
The [online reference] provides the most up-to-date, authoritative reference and |
|
|
have plenty of good testing examples. However, there are a few points that might |
|
|
not be immediately obvious from the reference, so those are highlighted here. |
|
|
|
|
|
## 1. Test any Node module that returns an exit code |
|
|
|
|
|
Create an empty test file named `a.js`: |
|
|
|
|
|
```text |
|
|
touch a.js |
|
|
``` |
|
|
|
|
|
Run the following command: |
|
|
|
|
|
```text |
|
|
node --test a.js |
|
|
``` |
|
|
|
|
|
You should see output similar to the following: |
|
|
|
|
|
```text |
|
|
$ node --test a.js |
|
|
TAP version 13 |
|
|
# Subtest: /path/to/a.js |
|
|
ok 1 - /path/to/a.js |
|
|
--- |
|
|
duration_ms: 0.042418125 |
|
|
... |
|
|
1..1 |
|
|
# tests 1 |
|
|
# pass 1 |
|
|
# fail 0 |
|
|
# cancelled 0 |
|
|
# skipped 0 |
|
|
# todo 0 |
|
|
# duration_ms 0.074304125 |
|
|
``` |
|
|
|
|
|
Now modify `a.js` so that the module exits with a **non-zero** exit code: |
|
|
|
|
|
```javascript |
|
|
// Test will fail with any non-zero exit code |
|
|
process.exit(1); |
|
|
``` |
|
|
|
|
|
You should see output similar to the following: |
|
|
|
|
|
```text |
|
|
$ node --test a.js |
|
|
TAP version 13 |
|
|
# Subtest: /path/to/a.js |
|
|
not ok 1 - /path/to/a.js |
|
|
--- |
|
|
duration_ms: 0.040703959 |
|
|
failureType: 'subtestsFailed' |
|
|
exitCode: 1 |
|
|
stdout: '' |
|
|
stderr: '' |
|
|
error: 'test failed' |
|
|
code: 'ERR_TEST_FAILURE' |
|
|
... |
|
|
1..1 |
|
|
# tests 1 |
|
|
# pass 0 |
|
|
# fail 1 |
|
|
# cancelled 0 |
|
|
# skipped 0 |
|
|
# todo 0 |
|
|
# duration_ms 0.0623685 |
|
|
|
|
|
``` |
|
|
|
|
|
You can test multiple modules explicitly. For example, with two separate test |
|
|
modules (one that returns a non-zero exit code), you would see output similar to |
|
|
this: |
|
|
|
|
|
```text |
|
|
$ node --test a.js b.js |
|
|
TAP version 13 |
|
|
# Subtest: /path/to/a.js |
|
|
not ok 1 - /path/to/a.js |
|
|
--- |
|
|
duration_ms: 0.040011959 |
|
|
failureType: 'subtestsFailed' |
|
|
exitCode: 1 |
|
|
stdout: '' |
|
|
stderr: '' |
|
|
error: 'test failed' |
|
|
code: 'ERR_TEST_FAILURE' |
|
|
... |
|
|
# Subtest: /path/to/b.js |
|
|
ok 2 - /path/to/b.js |
|
|
--- |
|
|
duration_ms: 0.038038583 |
|
|
... |
|
|
1..2 |
|
|
# tests 2 |
|
|
# pass 1 |
|
|
# fail 1 |
|
|
# cancelled 0 |
|
|
# skipped 0 |
|
|
# todo 0 |
|
|
# duration_ms 0.063258125 |
|
|
``` |
|
|
|
|
|
## 2. Include tests using naming pattern |
|
|
|
|
|
In the previous section, tests were specified explicitly. You can read the |
|
|
[online reference] for specifics, but generally `node --test` will find and |
|
|
execute tests if any of the following naming patterns are used: |
|
|
|
|
|
* Files are named any of: |
|
|
* `test.EXT` |
|
|
* `test-NAME.EXT` |
|
|
* `NAME.test.EXT` | `NAME-test.EXT` | `NAME_test.EXT` |
|
|
* Any `NAME.EXT` under a `test` directory, recursively. |
|
|
|
|
|
Where `EXT` is one of `js`|`cjs`|`mjs`. |
|
|
|
|
|
|
|
|
## 3. Writing test functions |
|
|
|
|
|
Create `test/test.js`: |
|
|
|
|
|
```javascript |
|
|
import test from "node:test"; |
|
|
import {strict as assert} from "node:assert"; |
|
|
``` |
|
|
|
|
|
Add a test function: |
|
|
|
|
|
```javascript |
|
|
test("should always be true", () => { |
|
|
assert(true); |
|
|
}); |
|
|
``` |
|
|
|
|
|
And test: |
|
|
|
|
|
```text |
|
|
$ node --test |
|
|
TAP version 13 |
|
|
# Subtest: /path/to/test/test.js |
|
|
ok 1 - /path/to/test/test.js |
|
|
--- |
|
|
duration_ms: 0.048303625 |
|
|
... |
|
|
1..1 |
|
|
# tests 1 |
|
|
# pass 1 |
|
|
# fail 0 |
|
|
# cancelled 0 |
|
|
# skipped 0 |
|
|
# todo 0 |
|
|
# duration_ms 0.075651083 |
|
|
``` |
|
|
|
|
|
## 4. Using the TestContext |
|
|
|
|
|
You can use the `TestContext` object supplied to your test callback. One |
|
|
potentially useful method is `TestContext.diagnostic`, shown below (presumably |
|
|
you would use this to provide more useful information as diagnostic output of |
|
|
your test code than would be summarized as part of a custom assert error |
|
|
message). The diagnostic messages appear after the stack trace for the |
|
|
failed test results. |
|
|
|
|
|
```javascript |
|
|
test("should be 5", t => { |
|
|
t.diagnostic("***DIAGNOSTIC: about to assign val"); |
|
|
const val = 2 + 2; |
|
|
//t.diagnostic(`***DIAGNOSTIC: val=${val}`); |
|
|
assert.equal(5, val); |
|
|
}); |
|
|
``` |
|
|
|
|
|
Tests can be nested like this: |
|
|
|
|
|
```javascript |
|
|
test("test suite", t => { |
|
|
t.test("a", t => { |
|
|
}); |
|
|
|
|
|
t.test("b", t => { |
|
|
}); |
|
|
|
|
|
}); |
|
|
``` |
|
|
|
|
|
## 5. Simplifying with describe and it |
|
|
|
|
|
If you're willing to give up access to the `TestContext` object, you can |
|
|
simplify writing test suites using `describe` and `it`. You can still nest |
|
|
a `test` under `describe`, if you want to: |
|
|
|
|
|
```javascript |
|
|
|
|
|
import test, {describe, it} from "node:test"; |
|
|
import {strict as assert} from "node:assert"; |
|
|
|
|
|
describe("test suite", () => { |
|
|
it("is always true", () => { |
|
|
assert(true); |
|
|
}); |
|
|
|
|
|
it("tests val", {skip: true}, () => { |
|
|
const val = 2 + 2; |
|
|
assert.equal(5, val); |
|
|
}); |
|
|
|
|
|
it("tests val", {todo: true}, () => { |
|
|
const val = 2 + 2; |
|
|
assert.equal(5, val); |
|
|
}); |
|
|
|
|
|
test("val is 5", t => { |
|
|
t.diagnostic("***DIAGNOSTIC: about to assign val"); |
|
|
const val = 2 + 2; |
|
|
t.diagnostic(`***DIAGNOSTIC: val=${val}`); |
|
|
assert.equal(5, val); |
|
|
}); |
|
|
}); |
|
|
``` |
|
|
|
|
|
<!-- references --> |
|
|
|
|
|
[online reference]: https://nodejs.org/api/test.html |
|
|
[TAP]: https://testanything.org/ |