# 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()`.

{/* 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-expect-assertions" = "error" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"bun:test/prefer-expect-assertions" = ["error", {
  onlyFunctionsWithAsyncKeyword = false,
  onlyFunctionsWithExpectInCallback = false,
  onlyFunctionsWithExpectInLoop = false,
}]
```

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

## Examples

Reported:

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

test("rejects a wrong password", async () => {
  try {
    await login("ada", "wrong");
  } catch (error) {
    expect(error).toBeInstanceOf(AuthError);
  }
});
```

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

test("adds", () => {
  expect.assertions("1");
  expect(1 + 1).toBe(2);
});
```

Not reported:

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

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

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

test("emits the saved user", () => {
  store.on("save", (user) => {
    expect(user.name).toBe("Ada");
  });
  store.save({ name: "Ada" });
});
```

{/* good {"options": [{"onlyFunctionsWithExpectInCallback": true}]} */}
```ts
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.
