Skip to content

Is the promise returned from suite or test ever useful in userland? #5122

@fabiancook

Description

@fabiancook

Node.js Version

v24.12.0

NPM Version

v11.6.2

Operating System

Darwin

Subsystem

Other

Description

There appears to be scenarios where the returned promise from suite causes the node test runner to hang.

I then found that I could drop the await from suite and it was good to go. I questioned why I was using await to begin with here, which lead me to the definitely typed types where the return of suite and test is documented as Promise<void>.

I then read through the documentation which showed that no await is ever used with suite in examples, and then I also see in node's own use of the test runner await is excluded.

I was looking through how suite was implemented to be sure that a promise was returned, and yeah, there is a path there from suite to a function that is async (Test.prototype.run, called by Test.prototype.start), this answers that the await is somewhat genuine still on these functions.


The actual question here I have for help is, is the return promise for these functions ever actually useful for a consumer of these tests? Or are they a foot-gun instead given things like concurrency being incoming (but experimental)?

Is there a reason why at least the top level suite wouldn't be void'd instead of the promise returned? Allowing then the process to receive any unhandled rejection or for the test runner to do its thing.

e.g.

  function run(name, options, fn, overrides) {
    const parent = testResources.get(executionAsyncId()) || lazyBootstrapRoot();
    const subtest = parent.createSubtest(Factory, name, options, fn, overrides);
    if (parent instanceof Suite) {
      return;
    }
    void startSubtestAfterBootstrap(subtest);
  }

I get test returning a promise and being usable where different tests need to be aligned, but it feels more of a job for sub tests within a test context.

PS. I haven't created a bug report here for the test-isolation flag causing the problem case where this question came up (I should open this though following this general help)

Minimal Reproduction

node --test-concurrency 1 --test-isolation none --test src/**/*.spec.js

src/index.await.spec.js:

import { suite, test } from "node:test";

await suite("Suite", async () => {
  await test("Test", async (t) => {
    await t.test("Subtest", async () => {
      await fetch("https://example.com");
    });
  });
});

src/index.no-await.spec.js:

import { suite, test } from "node:test";

suite("Suite", async () => {
  test("Test", async (t) => {
    await t.test("Subtest", async () => {
      await fetch("https://example.com");
    });
  });
});

Output

On force exiting the process:

$ node --test-concurrency 1 --test-isolation none --test src/index.await.spec.js
^C▶ Suite
  ✖ Test
✖ Suite
ℹ tests 1
ℹ suites 1
ℹ pass 0
ℹ fail 0
ℹ cancelled 1
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11228.317959

✖ failing tests:

test at src/index.await.spec.js:4:9
✖ Test
  'Promise resolution is still pending but the event loop has already resolved

When not including await:

$ node --test-concurrency 1 --test-isolation none --test src/index.no-await.spec.js
▶ Suite
  ▶ Test
    ✔ Subtest (734.810959ms)
  ✔ Test (735.376334ms)
✔ Suite (735.687584ms)
ℹ tests 2
ℹ suites 1
ℹ pass 2
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 820.4665

When using await and defaults:

$ node --test src/index.await.spec.js
▶ Suite
  ▶ Test
    ✔ Subtest (628.745958ms)
  ✔ Test (629.537417ms)
✔ Suite (629.883ms)
ℹ tests 2
ℹ suites 1
ℹ pass 2
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 742.470041

Before You Submit

  • I have looked for issues that already exist before submitting this
  • My issue follows the guidelines in the README file, and follows the 'How to ask a good question' guide at https://stackoverflow.com/help/how-to-ask

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions