denoland/deno · error · Error

test was expected to fail but passed

Error message

test was expected to fail but passed

What it means

A test registered with the expectFailure option inverts the pass/fail semantics: the runner expects the body to throw (runPossiblyExpectingFailure catches and, when expectFailure is a pattern/predicate, checks the error matches). If the body runs to completion without error, `failed` stays false and the runner throws 'test was expected to fail but passed'. So with this option, a passing test body is itself the failure.

Source

Thrown at ext/node/polyfills/testing.ts:1334

    }
  }

  let failed = false;
  try {
    await runWithTestGuards(
      () => runNodeTestFunction(fn, nodeTestContext),
      guards,
    );
    nodeTestContext._checkPlan();
  } catch (err) {
    failed = true;
    assertExpectedFailure(err, options.expectFailure);
  } finally {
    await nodeTestContext._drainSubtests();
  }

  if (!failed) {
    throw new Error("test was expected to fail but passed");
  }
  return undefined;
}

class TestPlan {
  #expected;
  #actual = 0;

  constructor(count) {
    this.#expected = count;
  }

  increment() {
    this.#actual++;
  }

  check() {
    if (this.#actual !== this.#expected) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. If the underlying bug is fixed, remove the expectFailure option and assert success directly
  2. If the test must still fail, make the body actually throw: assert.fail(), assert.throws(...), or await assert.rejects(...)
  3. Audit the body for swallowed errors — missing await on async calls, .catch(() => {}), or try/catch around the formerly-throwing line
  4. If expectFailure is a RegExp/string/predicate, confirm the intended failure actually matches it — and that it still occurs

Example fix

// before
test('parses bad input', { expectFailure: true }, () => {
  parse('bad'); // bug was fixed; no longer throws
});

// after
test('parses bad input', () => {
  assert.throws(() => parse('bad'), SyntaxError);
});
Defensive patterns

Strategy: validation

Validate before calling

// dev-time dry run: confirm the body actually throws before flagging expectFailure
async function expectFailTest(name, fn) {
  let threw = false;
  try {
    await fn();
  } catch {
    threw = true;
  }
  if (!threw) {
    throw new Error(`refusing to register expectFailure: '${name}' body does not throw`);
  }
  test(name, { expectFailure: true }, fn);
}

Prevention

When it happens

Trigger: test('name', { expectFailure: true }, fn) where fn completes normally; the same with expectFailure: /pattern/ or a predicate, since a body that does not throw produces no error to match; async bodies whose rejection is accidentally swallowed (missing await, empty catch).

Common situations: The bug the test documented got fixed, so the body no longer throws but the expectFailure flag was never removed; TDD red tests left flagged after going green; refactors that wrapped the throwing call in a try/catch or optional chaining that now hides the error.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/8d4db10e6b1dbec5. Report an issue: GitHub.