# 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.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[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.

```toml bunfig.toml
[lint.rules]
"bun:test/prefer-ending-with-an-expect" = ["error", {
  # assertFunctionNames = <list of strings>,
  additionalTestBlockFunctions = [], # list of strings
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```ts
import { expect, test } from "bun:test";

test("renames the user", async () => {
  const user = await createUser("Ada");
  expect(user.name).toBe("Ada");
  await user.rename("Grace");
});
```

{/* bad */}
```ts
import { expect, test } from "bun:test";

test("loads the user", () => {
  return expect(loadUser(1)).resolves.toBeDefined();
});
```

Not reported:

{/* good */}
```ts
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:

{/* good */}
```ts
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.

{/* good {"options": [{"assertFunctionNames": ["assert*", "request.**.expect"]}]} */}
```ts
import { test } from "bun:test";

test("has a valid user", () => {
  assertValidUser(loadUser(1));
});

test("responds with 200", async () => {
  await request(app).get("/users").expect(200);
});
```

{/* bad {"options": [{"assertFunctionNames": ["assert*"]}]} */}
```ts
import { test } from "bun:test";

test("has a valid user", () => {
  expectValidUser(loadUser(1));
});
```

With `additionalTestBlockFunctions`, your own wrapper or a hook is checked too:

{/* bad {"options": [{"additionalTestBlockFunctions": ["scenario"]}]} */}
```ts
import { expect } from "bun:test";
import { scenario } from "./helpers";

scenario("saves the user", async () => {
  expect(await saveUser()).toBe(true);
  await cleanup();
});
```

{/* bad {"options": [{"additionalTestBlockFunctions": ["afterEach"]}]} */}
```ts
import { afterEach, expect } from "bun:test";

afterEach(() => {
  expect(liveSockets()).toBe(0);
  resetDatabase();
});
```
