Node Test Runner: skip a test if an environment variable is missing/empty
The node:test
module is a built-in test runner and orchestrator in Node.js. It’s available as experimental since Node 18 but has stabilised in Node 20 and some features are still experimental at the time of writing, in Node 21.
node:test
supports test skipping throught multiple approaches but the two we’ll focus on here are the “programmatic skips”, where at runtime we can decide whether a test should be skipped or not.
This can be useful for integration or end to end testing scenarios being driven via the Node.js test runner (using node:test
).
Table of Contents
You can skip to the examples in the node-test-runner-examples
repository:
- using
{skip: VAR}
andt.skip('message')
syntax: node-test-runner-examples/src/01.03-programmatic-skip.test.js - a real-world example of using programmatic skipping to run a test on Node 21+ only: node-test-runner-examples/src/02.03-date-mock-timers.test.js
Using { skip: VARIABLE }
options/config to programmatically skip a test in node:test
The test
, it
and describe
exports from node:test
take an optional “options” parameter as its second parameter, ie. test('test-name', options, () => {})
.
You can read more about test()
options
parameter in the Node.js docs.
The full example for this section is available on GitHub: node-test-runner-examples/src/01.03-programmatic-skip.test.js.
Assuming we need REQUIRED_ENV_VARIABLE
to be set we can pass { skip: !process.env.REQUIRED_ENV_VARIABLE }
in order to skip test execution when the value is not populated.
import { test } from 'node:test';
import assert from 'node:assert/strict';
test(
'skips using config if environment variable is missing',
{ skip: !process.env.REQUIRED_ENV_VARIABLE },
() => {
assert.fail('if we get here, we need REQUIRED_ENV_VARIABLE and fail');
}
);
When we run this test, we get the following output:
node src/01.03-programmatic-skip.test.js
﹣ skips using config if environment variable is missing (0.558584ms) # SKIP
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 0
ℹ cancelled 0
ℹ skipped 1
ℹ todo 0
ℹ duration_ms 4.818875
Passing { skip: VAR }
as the options
to node:test
’s test
function allows us to express simple “skip conditions”.
We’ve now seen how to use the Node.js test()
options
parameter to skip a test programmatically based on the value of an environment variable. Next we’ll see how to use t.skip()
to achieve the same outcome from the body of the test.
Using t.skip
to programmatically skip a test in node:test
The full example for this section is available on GitHub: node-test-runner-examples/src/01.03-programmatic-skip.test.js.
The test
function of node:test
passes a “test context” to the “test body”. This test context is usually called t
in the test implementation function and it contains a .skip()
function.
We can therefore write the following test, where we don’t use options.skip = true
as in the previous example, instead we write an if()
condition inside the test and use t.skip()
if the condition is matched.
import { test } from 'node:test';
import assert from 'node:assert/strict';
// no change to the other test
test('skips programmatically if environment variable is missing', (t) => {
if (!process.env.REQUIRED_ENV_VARIABLE) {
return t.skip('REQUIRED_ENV_VARIABLE is missing');
}
assert.fail(
'If we got here the ".failing" modifier would not be satisfied since the test passed'
);
});
When we run this test, we get the following output:
node src/01.03-programmatic-skip.test.js
﹣ skips programmatically if environment variable is missing (0.580583ms) # REQUIRED_ENV_VARIABLE is missing
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 0
ℹ cancelled 0
ℹ skipped 1
ℹ todo 0
ℹ duration_ms 4.781
We’ve now seen how to skip a test programmatically from inside the test function. Next we’ll see a real-world example of programmatic skipping of node:test
tests.
Programmatic skip a node:test
test based on Node.js version
The real-world scenario is the following: we have a test using a Node.js API with the API contract available in Node 21 but not Node 20.
To be more precise: the MockTimers API initially supported calls of the form mock.timers.enable(['setTimeout'])
in Node 20, but in Node 21 it’s been extended to also allow mocking of Date
/Date.now
etc. As part of this change, the mock.timers.enable()
call argument shape has changed to mock.timers.enable({ api: ['Date', 'setTimeout'], now: 1000 })
.
Therefore:
- In Node 20:
mock.timers.enable({})
is not supported - In Node 21+:
mock.timers.enable({})
is supported
See also:
To recap, what we want to do is skip a test that uses mock.timers.enable(Object)
on Node 20 and run it on Node 21+.
We can achieve this as follows.
First we extract the Node.js major version from process.versions.node
, we want the “major” part of MAJOR.MINOR.PATCH
so we .split('.')[0]
to achieve this. Next we compare that MAJOR
to see whether it’s less than or equal to "20"
, note that this is using lexicographical sort, it would be better to parse to an integer and do a number comparison.
Finally we use isNode20OrLower
as the skip
condition in our test, test('mock.timers can mock Date.now()', { skip: isNode20OrLower }, (t) => ...
.
See the full test:
import { test, mock, afterEach } from 'node:test';
import assert from 'node:assert/strict';
const getCurrentDate = () => new Date();
afterEach(() => {
mock.timers.reset();
});
const isNode20OrLower = process.versions.node.split('.')[0] <= '20';
test('mock.timers can mock Date.now()', { skip: isNode20OrLower }, (t) => {
t.mock.timers.enable({
apis: ['Date'],
now: new Date('2023-05-14T11:01:58.135Z'),
});
assert.deepEqual(Date.now(), 1684062118135);
});
When we run this code on Node 21 it runs:
node -v && node src/02.03-date-mock-timers.test.js
v21.5.0
✔ mock.timers can mock Date.now() (0.126667ms)
(node:4185) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 10.228166
When we run this code on Node 20 the test is skipped:
node -v && node src/02.03-date-mock-timers.test.js
v20.11.0
﹣ mock.timers can mock Date.now() (0.090208ms) # SKIP
(node:4288) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 0
ℹ cancelled 0
ℹ skipped 1
ℹ todo 0
ℹ duration_ms 6.732459
We’ve now seen a real-world example of programmatic test skipping.
Get The Jest Handbook (100 pages)
Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.
orJoin 1000s of developers learning about Enterprise-grade Node.js & JavaScript