oxc-project/oxc · warning · OxcDiagnostic

Prefer `toHaveBeenCalled()` over `toHaveBeenCalledTimes(0)`

Error message

Prefer `toHaveBeenCalled()` over `toHaveBeenCalledTimes(0)`

What it means

This is oxlint's 'jest/prefer-to-have-been-called' diagnostic. When an expect(...).toHaveBeenCalledTimes(0) (or toBeCalledTimes(0)) matcher is found on a parsed expect call, the rule asks for the intent-revealing forms: toHaveBeenCalled() for 'was called', not.toHaveBeenCalled() for 'was not called'. The zero-times form is a double negative that reviewers must decode.

Source

Thrown at crates/oxc_linter/src/rules/jest/prefer_to_have_been_called.rs:15

use crate::{
    context::LintContext,
    rule::Rule,
    utils::{ParsedExpectFnCall, PossibleJestNode, parse_expect_jest_fn_call},
};
use oxc_ast::{
    AstKind,
    ast::{CallExpression, Expression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

fn prefer_to_have_been_called_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Prefer `toHaveBeenCalled()` over `toHaveBeenCalledTimes(0)`")
        .with_help("Use `toHaveBeenCalled()` to check if function was called, or `not.toHaveBeenCalled()` to check if it wasn't called")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct PreferToHaveBeenCalled;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Suggests using `toHaveBeenCalled()` or `not.toHaveBeenCalled()` over `toHaveBeenCalledTimes(0)` or `toBeCalledTimes(0)`.
    ///
    /// ### Why is this bad?
    ///
    /// `toHaveBeenCalled()` is more explicit and readable than `toHaveBeenCalledTimes(0)`.
    ///
    /// ### Examples
    ///

View on GitHub (pinned to e1e7af627c)

Solutions

  1. For 'never called', negate the matcher: expect(fn).not.toHaveBeenCalled().
  2. For asserting calls with counts > 0, keep toHaveBeenCalledTimes(n) with n >= 1, or use toHaveBeenCalled() when the exact count does not matter.
  3. Bulk-migrate with search and replace for toHaveBeenCalledTimes(0) → not.toHaveBeenCalled().
  4. Disable the rule in .oxlintrc.json if the parameterized style is accepted team-wide.

Example fix

// before
expect(logger.warn).toHaveBeenCalledTimes(0);

// after
expect(logger.warn).not.toHaveBeenCalled();
Defensive patterns

Strategy: validation

Validate before calling

// find zero-times matchers before the rule does
const { execSync } = require('node:child_process');
console.log(execSync("rg -n 'to(Have|Be)CalledTimes\\(\\s*0\\s*\\)' tests/", { encoding: 'utf8' }));

Prevention

When it happens

Trigger: Enable the rule and lint a file containing expect(fn).toHaveBeenCalledTimes(0) or the toBeCalledTimes(0) alias — the matcher name is matched with its argument literal 0 during parse_expect_jest_fn_call processing. The diagnostic fires on the matcher span.

Common situations: Tests asserting 'this spy was never invoked' written quickly as toHaveBeenCalledTimes(0), and AI- or template-generated tests that default to the parameterized matcher. Enabling recommended Jest presets surfaces them during CI lint passes.

Related errors


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