no-async-foreach-assertions
Disallow async assertion callbacks passed to forEach in executable tests.
Rule detailsโ
Array.prototype.forEach ignores the promise returned by an async callback. In
tests, that means assertions inside the callback can run after the test has
already finished, or fail as an unhandled rejection instead of failing the test
case that scheduled them.
Use a for...of loop when each check should run sequentially, or use
await Promise.all(items.map(...)) when the checks can run in parallel.
Incorrectโ
it("checks every user", () => {
users.forEach(async (user) => {
await expect(loadProfile(user.id)).resolves.toMatchObject({
id: user.id,
});
});
});
test("checks every token", async () => {
tokens.forEach(async (token) => {
const result = await validateToken(token);
expect(result.valid).toBe(true);
});
});
Correctโ
it("checks every user", async () => {
for (const user of users) {
await expect(loadProfile(user.id)).resolves.toMatchObject({
id: user.id,
});
}
});
it("checks every user", async () => {
await Promise.all(
users.map(async (user) => {
await expect(loadProfile(user.id)).resolves.toMatchObject({
id: user.id,
});
})
);
});
What this rule reportsโ
This rule reports executable it(...) and test(...) callbacks that contain a
forEach(...) call with an async callback whose directly executed body contains
an expect(...) assertion. It also handles computed ["forEach"] access.
The rule does not report:
- synchronous
forEachassertion callbacks; - async
forEachcallbacks that do not contain assertions; - assertions hidden inside nested helper function definitions;
for...ofloops orawait Promise.all(items.map(...))patterns.
Optionsโ
This rule has no options.
When not to use itโ
This rule is normally safe for Jest- and Vitest-style test suites. Disable it
only for nonstandard test runners that deliberately capture promises returned by
forEach callbacks, which regular JavaScript arrays do not do.
Rule catalog ID: R013