assert.match()

Tests a string against a regular expression and throws an AssertionError when it does not match.

Signature
assert.match(string, regexp[, message])
  • Added in Node.js v13.6.0, v12.16.0

Parameters

ParameterTypeDescription
$stringstringThe input to test. Must actually be a string; any other type fails the assertion.
$regexpRegExpThe regular expression the input must match. Must be a RegExp instance, not a pattern string.
$messageoptionalstring | Error | FunctionError message, an Error instance to throw instead, or a function called on failure.

Errors

Throws an AssertionError with code ERR_ASSERTION when the input does not match, or when string is not a string. Passing a non-RegExp as regexp throws a TypeError with code ERR_INVALID_ARG_TYPE.

Changelog

  • PHP v26.0.0Message may now be a printf-like format string or function.
  • PHP v16.0.0This API is no longer experimental.

assert.match() is the assertion to reach for when a value is shaped rather than fixed: a generated id, a version string, a formatted log line, a stack trace, an error message with a path in it. Comparing those with assert.strictEqual() makes a brittle test; matching a pattern pins what actually matters.

Its advantage over assert.ok(re.test(s)) is entirely in the failure output: ok() can only say false, while match() prints the pattern and the offending input.

Argument order and types

The string comes first, the RegExp second — the reverse of regexp.test(string), which is an easy slip. Both types are enforced: a non-string first argument fails the assertion with ERR_ASSERTION, and a pattern passed as a plain string throws a TypeError with ERR_INVALID_ARG_TYPE rather than being compiled for you.

Anchor deliberately

An unanchored pattern is a substring search, so /\d+/ matches 'v22 beta'. Add ^ and $ when you mean the whole string. Avoid the g flag: a global RegExp carries lastIndex between calls, so reusing one across several assertions can make the second one fail for no visible reason.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Assert a generated id or token conforms to its expected format
  • Check a log line or CLI output contains the right message without pinning timestamps
  • Verify an error message mentions the offending filename

Frequently asked questions

What is the argument order for assert.match()?

The string comes first and the RegExp second: assert.match(value, /pattern/). This is the reverse of regexp.test(string), which is the most common mistake — swapping them throws ERR_INVALID_ARG_TYPE.

Can I pass a string pattern to assert.match()?

No. The regexp argument must be a RegExp instance; a plain string throws a TypeError with code ERR_INVALID_ARG_TYPE. Build the pattern with new RegExp('...') if you need to assemble it dynamically.

Why does assert.match() fail the second time with the same string?

Your RegExp almost certainly has the /g flag, which makes it stateful — lastIndex is left after the first match and the next test starts from there. Remove the flag or create a new RegExp for each assertion.

More examples

Pinning the shape of a generated value

The anchored pattern pins the prefix, the UUID v4 layout and the total structure without depending on the random bytes, and the printed length is stable at 40 characters. The second assertion demonstrates the default substring behaviour, which is why the first one needs its ^ and $.

Example · javascript
const assert = require('node:assert');
const crypto = require('node:crypto');

function newRequestId() {
  return `req_${crypto.randomUUID()}`;
}

const id = newRequestId();

// strictEqual is impossible — the value changes every call.
assert.match(id, /^req_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
console.log('id has the expected shape, length', id.length); // 40

// Unanchored patterns are substring searches — this passes too:
assert.match('prefix-' + id + '-suffix', /req_/);
console.log('unanchored pattern matched inside a longer string');

The /g flag trap

The global RegExp retains lastIndex = 4 after the first successful test, so the second call starts searching past the match and reports a failure on identical input. Dropping the g flag — or building the RegExp inside the assertion — removes the shared state entirely.

Example · javascript
const assert = require('node:assert');

const globalRe = /node/g;

assert.match('node runtime', globalRe);
console.log('lastIndex after first match:', globalRe.lastIndex); // 4

try {
  assert.match('node runtime', globalRe); // same string, now fails
} catch (err) {
  console.log('second identical assertion failed:', err.code);
}

// A fresh, non-global RegExp behaves consistently.
const safeRe = /node/;
assert.match('node runtime', safeRe);
assert.match('node runtime', safeRe);
console.log('non-global pattern matched twice');

Inside the built-in test runner

The first test asserts the whole line format while leaving the timestamp free, which is the usual reason to prefer match over strictEqual. The second shows match used inside a throws predicate to check that the error text names the file, with the stable code property asserted exactly.

Example · javascript
const test = require('node:test');
const assert = require('node:assert/strict');

function formatLog(level, msg, time) {
  return `[${time.toISOString()}] ${level.toUpperCase()} ${msg}`;
}

test('formatLog produces a parseable line', () => {
  const line = formatLog('warn', 'disk 91% full', new Date());

  assert.match(line, /^\[\d{4}-\d{2}-\d{2}T[\d:.]+Z\] WARN disk 91% full$/);
});

test('error message names the offending file', () => {
  assert.throws(
    () => require('node:fs').readFileSync('./no-such-file.json'),
    (err) => {
      assert.match(err.message, /no-such-file\.json/);
      assert.strictEqual(err.code, 'ENOENT');
      return true;
    },
  );
});

// $ node --test
// ✓ formatLog produces a parseable line (1.0ms)
// ✓ error message names the offending file (1.6ms)

Discussion

  • Be the first to comment on this lesson.