oxc-project/oxc · warning · OxcDiagnostic

Test must end with an assertion

Error message

Test must end with an assertion

What it means

This is oxlint's 'jest/prefer-ending-with-an-expect' diagnostic. It checks the body of each test/hook block and reports when the final statement is not an assertion call (expect(...) or a configured assert function). Tests that end with setup code, logging, or a dangling promise can pass without verifying anything, so the rule requires the last statement to be an assertion; the help says to add an expect as the last statement.

Source

Thrown at crates/oxc_linter/src/rules/jest/prefer_ending_with_an_expect.rs:24

use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use oxc_str::CompactStr;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    context::LintContext,
    rule::{DefaultRuleConfig, Rule},
    rules::PossibleJestNode,
    utils::{
        JestGeneralFnKind, convert_pattern, get_node_name, matches_assert_function_name,
        parse_expect_jest_fn_call, parse_general_jest_fn_call,
    },
};

fn prefer_ending_with_an_expect_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Test must end with an assertion")
        .with_help("Add an `expect` or assertion call as the last statement in the test block.")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct PreferEndingWithAnExpect(Box<PreferEndingWithAnExpectConfig>);

#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct PreferEndingWithAnExpectConfig {
    /// An array of function names that should also be treated as test blocks.
    additional_test_block_functions: Vec<CompactStr>,
    /// A list of function names that should be treated as assertion functions.
    /// Default: `["expect"]`
    #[serde(deserialize_with = "deserialize_assert_function_names")]
    #[schemars(with = "Vec<String>")]
    assert_function_names: Vec<Regex>,
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Add the missing expect(...) as the final statement of the test body: expect(result).toBe(true).
  2. Reorder statements so the assertion is last (move logging or cleanup before it, or into afterEach hooks).
  3. Move shared setup/cleanup out of the test body into before/after hooks so the test can end with its assertion.
  4. For tests that legitimately cannot assert (smoke tests), disable the rule inline with // oxlint-disable-next-line jest/prefer-ending-with-an-expect.

Example fix

// before
it('saves the user', async () => {
  const repo = new Repo();
  await repo.save({ id: 1 });
});

// after
it('saves the user', async () => {
  const repo = new Repo();
  await repo.save({ id: 1 });
  expect(await repo.find(1)).toBeDefined();
});
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Enable the rule and lint a test whose body's last statement is anything other than a call expression matching the expect/assert name patterns (configurable via additionalTestBlockFunctions and assertion matching utils). E.g. it('x', () => { setup(); save(user); }) triggers it.

Common situations: Fire-and-forget tests (calling an API without asserting the result), tests where the assertion sits inside an if that may be skipped, and tests ending with console.log debugging leftovers. Rule sets from strict presets flag these during CI, often surprising developers whose assertions are second-to-last after a stray statement.

Related errors


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