bun:test/prefer-ending-with-an-expect
Require the last statement of a test to be an assertion
Anything a test does after its last assertion isn't checked. If the test ends with a click, a request or a save, that step can do the wrong thing and the test still passes.
The test's last statement must be a call to expect, with or without await. A test with an empty body is reported. So is one that ends with an if, a loop or a return, even if there's an expect inside.
Configure#
Off by default. To turn it on:
[lint.rules]
"bun:test/prefer-ending-with-an-expect" = "error" # or "warn"Every option, set to its default. The ones that are commented out are unset by default.
[lint.rules]
"bun:test/prefer-ending-with-an-expect" = ["error", {
# assertFunctionNames = <list of strings>,
additionalTestBlockFunctions = [], # list of strings
}]Examples#
import { expect, test } from "bun:test";
testTest must end with an assertion("renames the user", async () => {
const user = await createUser("Ada");
expect(user.name).toBe("Ada");
await user.rename("Grace");
});import { expect, test } from "bun:test";
testTest must end with an assertion("loads the user", () => {
return expect(loadUser(1)).resolves.toBeDefined();
});import { expect, test } from "bun:test";
test("renames the user", async () => {
const user = await createUser("Ada");
await user.rename("Grace");
expect(user.name).toBe("Grace");
});
test("loads the user", async () => {
await expect(loadUser(1)).resolves.toBeDefined();
});Options#
| Option | Default | What it does |
|---|---|---|
assertFunctionNames | any name that contains expect | The calls that count as an assertion, besides expect itself. |
additionalTestBlockFunctions | [] | Other functions to check like test, such as your own wrappers around it or a hook like afterEach. |
By default, a helper or a method with expect in its name counts:
import { test } from "bun:test";
test("has a valid user", () => {
expectValidUser(loadUser(1));
});
test("responds with 200", async () => {
await request(app).get("/users").expect(200);
});With assertFunctionNames, only expect and the names in the list count. A name can be a dotted path. * matches any characters in one part of the path, and ** matches any number of parts, including none.
{ "assertFunctionNames": ["assert*", "request.**.expect"] }No problemsimport { test } from "bun:test";
test("has a valid user", () => {
assertValidUser(loadUser(1));
});
test("responds with 200", async () => {
await request(app).get("/users").expect(200);
});{ "assertFunctionNames": ["assert*"] }1 problemimport { test } from "bun:test";
testTest must end with an assertion("has a valid user", () => {
expectValidUser(loadUser(1));
});With additionalTestBlockFunctions, your own wrapper or a hook is checked too:
{ "additionalTestBlockFunctions": ["scenario"] }1 problemimport { expect } from "bun:test";
import { scenario } from "./helpers";
scenarioTest must end with an assertion("saves the user", async () => {
expect(await saveUser()).toBe(true);
await cleanup();
});{ "additionalTestBlockFunctions": ["afterEach"] }1 problemimport { afterEach, expect } from "bun:test";
afterEachTest must end with an assertion(() => {
expect(liveSockets()).toBe(0);
resetDatabase();
});