oxc-project/oxc · warning

Prefer `toHaveBeenCalledTimes()` over `toHaveLength()` when

Error message

Prefer `toHaveBeenCalledTimes()` over `toHaveLength()` when asserting mock call counts

What it means

Warning from the oxlint `jest/prefer-to-have-been-called-times` rule (source: crates/oxc_linter/src/rules/shared/jest_vitest/prefer_to_have_been_called_times.rs:16). The check fires only when the expect argument is exactly a `...mock.calls` member expression (verified by `is_expect_argument_mock_calls` in the source) and the matcher is `toHaveLength`; it suggests `toHaveBeenCalledTimes()` and registers an automatic fix that rewrites the whole call expression, preserving modifiers like `not`.

Source

Thrown at crates/oxc_linter/src/rules/shared/jest_vitest/prefer_to_have_been_called_times.rs:16

use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{GetSpan, Span};

use crate::{
    context::LintContext,
    fixer::RuleFixer,
    utils::{ParsedExpectFnCall, PossibleJestNode, parse_expect_jest_fn_call},
};
use oxc_ast::{
    AstKind,
    ast::{CallExpression, Expression, MemberExpression},
    match_member_expression,
};

fn prefer_to_have_been_called_times_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(
        "Prefer `toHaveBeenCalledTimes()` over `toHaveLength()` when asserting mock call counts",
    )
    .with_help(
        "Use `toHaveBeenCalledTimes()` to assert the number of times a mock function was called",
    )
    .with_label(span)
}

pub const DOCUMENTATION: &str = r"### What it does

In order to have a better failure message, [`toHaveBeenCalledTimes` should be used
instead of directly checking the length of `mock.calls`](https://github.com/jest-community/eslint-plugin-jest/blob/v29.5.0/docs/rules/prefer-to-have-been-called-times.md).

### Why is this bad?

This rule triggers a warning if `toHaveLength` is used to assert the number of times a mock is called.

### Examples

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Accept the autofix: run `oxlint --fix` (the rule's fixer rewrites `expect(fn.mock.calls).toHaveLength(n)` to `expect(fn).toHaveBeenCalledTimes(n)` including `not` modifiers).
  2. Or edit manually: move the mock function itself into `expect` and swap the matcher - `expect(fn).toHaveBeenCalledTimes(n)`.
  3. For zero-call assertions also consider `expect(fn).not.toHaveBeenCalled()`.
  4. Suppress with `// oxlint-disable-next-line jest/prefer-to-have-been-called-times` when asserting on `.mock.calls` as data is intentional.

Example fix

// before
expect(someFunction.mock.calls).toHaveLength(1);

// after
expect(someFunction).toHaveBeenCalledTimes(1);
Defensive patterns

Strategy: validation

Validate before calling

// rg -n "\.mock\.calls\)\.toHaveLength\(" tests/ -t ts -t js

Prevention

When it happens

Trigger: `expect(fn.mock.calls).toHaveLength(1)`, `expect(fn.mock.calls).toHaveLength(0)`, or `expect(fn.mock.calls).not.toHaveLength(1)`. It does NOT fire for `fn.mock.calls[0][0]` argument assertions - the source requires the final member to be `calls` on a `mock` member.

Common situations: Mock call-count assertions written before authors learned `toHaveBeenCalledTimes`; vitest suites that copied jest patterns; enabling this rule repo-wide and hitting dozens of occurrences in older spec files.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/d4f083d14c7dbfe6. Report an issue: GitHub.