Linter · Rules
bun:test/prefer-expect-assertions
Require expect.assertions() or expect.hasAssertions() at the start of every test
An expect in a catch, a callback or a loop might never run, and a test where no expect runs still passes. expect.hasAssertions() makes the test fail unless at least one assertion ran. expect.assertions(2) makes it fail unless exactly two ran.
The first statement of every test must be one of the two. The rule also reports expect.hasAssertions() with an argument, and expect.assertions() with anything but one number literal.
--fix-suggestions adds expect.hasAssertions().
Configure
Off by default. To turn it on:
[lint.rules]
"bun:test/prefer-expect-assertions" = "error" # or "warn"
Every option, set to its default:
[lint.rules]
"bun:test/prefer-expect-assertions" = ["error", {
onlyFunctionsWithAsyncKeyword = false,
onlyFunctionsWithExpectInCallback = false,
onlyFunctionsWithExpectInLoop = false,
}]
Examples
Reported:
import { expect, test } from "bun:test";
test("rejects a wrong password", async () => {
try {
await login("ada", "wrong");
} catch (error) {
expect(error).toBeInstanceOf(AuthError);
}
});
import { expect, test } from "bun:test";
test("adds", () => {
expect.assertions("1");
expect(1 + 1).toBe(2);
});
Not reported:
import { expect, test } from "bun:test";
test("rejects a wrong password", async () => {
expect.assertions(1);
try {
await login("ada", "wrong");
} catch (error) {
expect(error).toBeInstanceOf(AuthError);
}
});
A beforeEach or afterEach hook that calls expect.hasAssertions() covers the tests after it in the same describe, or in the rest of the file if it's at the top level:
import { beforeEach, expect, test } from "bun:test";
beforeEach(() => {
expect.hasAssertions();
});
test("adds", () => {
expect(1 + 1).toBe(2);
});
Options
Without options, every test is checked. If you turn on one or more, only tests that match at least one of them are checked.
| Option | Default | What it does |
|---|---|---|
onlyFunctionsWithAsyncKeyword | false | Check tests that are async functions. |
onlyFunctionsWithExpectInCallback | false | Check tests that call expect inside a nested function. |
onlyFunctionsWithExpectInLoop | false | Check tests that call expect inside a loop. |
import { expect, test } from "bun:test";
test("emits the saved user", () => {
store.on("save", (user) => {
expect(user.name).toBe("Ada");
});
store.save({ name: "Ada" });
});
import { expect, test } from "bun:test";
test("adds", () => {
expect(1 + 1).toBe(2);
});
When not to use it
In a test that only calls expect at its top level, the extra line adds nothing. Use the options to check only the tests where an assertion can be skipped.