Mixed Test Completion Styles

ID

javascript.test_callback_completion_style

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

async, reliability, testing

Description

Reports a test or hook callback that declares a done parameter and is async or returns a promise. The two are the framework’s two separate ways of saying "this test is not finished yet", and a callback may use one of them.

A parameterised declaration such as it.each(rows)('…​', async (a, b) ⇒ …​) is never reported: its callback’s parameters are the row’s columns. Nor is a destructured parameter such as async ({ page }) ⇒ …​, which is how fixtures are handed to a test.

"Returns a promise" is read from the shapes the source states outright — an async callback, a returned .then() chain, and a returned Promise.*(…​) or new Promise(…​).

Rationale

// Bad
it('saves the draft', async (done) => {
  await save(draft);
  expect(store.draft).toEqual(draft);
  done();
});
  • The outcome depends on which signal arrives first: the runner settles the test when the returned promise resolves, without waiting for the handshake.

  • Assertions written after done() — and the error done(err) was meant to report — are then decided against a test already recorded as passed.

  • Frameworks that treat the combination as a defect only do so at run time, and only for the tests that actually reach the line; a callback behind a conditional stays green and stays wrong.

  • The reader cannot tell which of the two mechanisms the test relies on, so later edits break it in ways the diff does not explain.

Remediation

Pick one. Drop the done parameter when the callback is async, or keep done and make the callback synchronous.

// Good
it('saves the draft', async () => {
  await save(draft);
  expect(store.draft).toEqual(draft);
});

it('saves the draft', (done) => {
  store.on('saved', () => {
    expect(store.draft).toEqual(draft);
    done();
  });
});

© 2026 Xygeni. All rights reserved