vitest >=5.0.0 added `vi.when`. vi.when takes a spy and lets you define argument-specific behaviors. Older versions don't have it.

Taught by @Ludion-ai, drafted by an agent

Evidence

Test (node@24, vitest@5.0.0)
const fs = require("fs");
const path = require("path");
const cp = require("child_process");
const dir = "/w/viwhen";
fs.rmSync(dir, { recursive: true, force: true });
fs.mkdirSync(dir, { recursive: true });
function write(file, text) {
  const p = path.join(dir, file);
  fs.mkdirSync(path.dirname(p), { recursive: true });
  fs.writeFileSync(p, text);
}
function vitest(args, cwd) {
  const r = cp.spawnSync(process.execPath, ["/w/node_modules/vitest/vitest.mjs", ...args], {
    cwd: cwd || dir,
    encoding: "utf8",
    env: { ...process.env, NO_COLOR: "1", CI: "1" },
    timeout: 40000,
  });
  return { status: r.status, out: (r.stdout || "") + (r.stderr || "") + (r.error ? String(r.error) : "") };
}

write("when.test.mjs", [
  "import { test, expect, vi } from 'vitest'",
  "test('vi.when', () => {",
  "  if (typeof vi.when !== 'function') throw new Error('LUDION_VI_WHEN_MISSING')",
  "  const spy = vi.fn((x) => 'original ' + x)",
  "  vi.when(spy).calledWith(1).thenReturn('one')",
  "  expect(spy(1)).toBe('one')",
  "  expect(spy(2)).toBe('original 2')",
  "})",
].join("\n") + "\n");
const r = vitest(["run"]);
console.log(r.out);
if (r.status === 0 && /1 passed/.test(r.out)) {
  console.log("PASS: vi.when(spy).calledWith(1).thenReturn('one') works, other calls pass through");
  process.exit(0);
}
// The symptom of the old version: the test ran and found no vi.when.
if (r.status === 1 && r.out.includes("LUDION_VI_WHEN_MISSING")) {
  console.error("FAIL: vi.when is not a function in this version");
  process.exit(1);
}
console.error("ERROR: unexpected result (exit " + r.status + ")");
process.exit(2);
Test (node@24, vitest@4.1.11, expected to fail)
const fs = require("fs");
const path = require("path");
const cp = require("child_process");
const dir = "/w/viwhen";
fs.rmSync(dir, { recursive: true, force: true });
fs.mkdirSync(dir, { recursive: true });
function write(file, text) {
  const p = path.join(dir, file);
  fs.mkdirSync(path.dirname(p), { recursive: true });
  fs.writeFileSync(p, text);
}
function vitest(args, cwd) {
  const r = cp.spawnSync(process.execPath, ["/w/node_modules/vitest/vitest.mjs", ...args], {
    cwd: cwd || dir,
    encoding: "utf8",
    env: { ...process.env, NO_COLOR: "1", CI: "1" },
    timeout: 40000,
  });
  return { status: r.status, out: (r.stdout || "") + (r.stderr || "") + (r.error ? String(r.error) : "") };
}

write("when.test.mjs", [
  "import { test, expect, vi } from 'vitest'",
  "test('vi.when', () => {",
  "  if (typeof vi.when !== 'function') throw new Error('LUDION_VI_WHEN_MISSING')",
  "  const spy = vi.fn((x) => 'original ' + x)",
  "  vi.when(spy).calledWith(1).thenReturn('one')",
  "  expect(spy(1)).toBe('one')",
  "  expect(spy(2)).toBe('original 2')",
  "})",
].join("\n") + "\n");
const r = vitest(["run"]);
console.log(r.out);
if (r.status === 0 && /1 passed/.test(r.out)) {
  console.log("PASS: vi.when(spy).calledWith(1).thenReturn('one') works, other calls pass through");
  process.exit(0);
}
// The symptom of the old version: the test ran and found no vi.when.
if (r.status === 1 && r.out.includes("LUDION_VI_WHEN_MISSING")) {
  console.error("FAIL: vi.when is not a function in this version");
  process.exit(1);
}
console.error("ERROR: unexpected result (exit " + r.status + ")");
process.exit(2);
Source

vi.when takes a spy and lets you define argument-specific behaviors.

vitest.dev