This is an issue when a test uses a promise-returning assertion or expectation without awaiting it or returning it to the test framework.
Test frameworks determine whether a test passed or failed when the test function completes. When an assertion returns a promise, the framework must wait for that promise before it can evaluate the assertion result.
If the promise is neither awaited nor returned, the test can finish before the assertion runs. The framework sees no failure and reports the test as passed. The assertion then runs later, after the test has already completed.
This can lead to false positives:
In JavaScript/TypeScript, this includes Jest and Vitest .resolves and .rejects, Jasmine expectAsync(), and
Playwright’s async assertions.
Unreliable tests that pass even when they should fail can mask critical bugs in production code. Teams lose confidence in their test suite, and bugs may reach production. The effort spent writing tests provides no actual safety net.
Jest’s .resolves and .rejects assertions return promises. Await the assertion, or return it from the test.
describe("User API", () => {
it("fetches user successfully", () => {
expect(fetchUser(1)).resolves.toHaveProperty("name"); // Noncompliant
});
it("rejects on invalid ID", () => {
expect(fetchUser(-1)).rejects.toThrow("Invalid ID"); // Noncompliant
});
});
describe("User API", () => {
it("fetches user successfully", () => {
return expect(fetchUser(1)).resolves.toHaveProperty("name");
});
it("rejects on invalid ID", async () => {
await expect(fetchUser(-1)).rejects.toThrow("Invalid ID");
});
});
Vitest’s .resolves and .rejects assertions return promises. Await the assertion, or return it from the test.
import { it, expect } from "vitest";
it("processes data", () => {
expect(processData()).resolves.toEqual([1, 2, 3]); // Noncompliant
});
import { it, expect } from "vitest";
it("processes data", () => {
return expect(processData()).resolves.toEqual([1, 2, 3]);
});
Jasmine’s expectAsync() returns a promise. Await the assertion, or return it from the spec.
it("loads configuration", () => {
expectAsync(loadConfig()).toBeResolvedTo({ port: 3000 }); // Noncompliant
});
it("loads configuration", () => {
return expectAsync(loadConfig()).toBeResolvedTo({ port: 3000 });
});
Playwright’s auto-retrying assertions are async and must be awaited.
import { test, expect } from "@playwright/test";
test("displays welcome message", async ({ page }) => {
await page.setContent("<h1>Welcome</h1>");
expect(page.getByRole("heading")).toHaveText("Welcome"); // Noncompliant
});
import { test, expect } from "@playwright/test";
test("displays welcome message", async ({ page }) => {
await page.setContent("<h1>Welcome</h1>");
await expect(page.getByRole("heading")).toHaveText("Welcome");
});