{"id":"7e29d9b6c6168bb6","repo":"vitest-dev/vitest","slug":"formatcalls-spy-msg-args","errorCode":null,"errorMessage":"${formatCalls(spy, msg, args)}","messagePattern":"\\$\\{formatCalls\\(spy, msg, args\\)\\}","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"packages/expect/src/jest-expect.ts","lineNumber":614,"sourceCode":"      jestEquals(aItem, b[i], [...customTesters, iterableEquality]),\n    )\n  }\n\n  def(['toHaveBeenCalledWith', 'toBeCalledWith'], function (...args) {\n    const spy = getSpy(this)\n    const spyName = spy.getMockName()\n    const pass = spy.mock.calls.some(callArg => equalsArgumentArray(callArg, args))\n    const isNot = utils.flag(this, 'negate') as boolean\n\n    const msg = utils.getMessage(this, [\n      pass,\n      `expected \"${spyName}\" to be called with arguments: #{exp}`,\n      `expected \"${spyName}\" to not be called with arguments: #{exp}`,\n      args,\n    ])\n\n    if ((pass && isNot) || (!pass && !isNot)) {\n      throw new AssertionError(formatCalls(spy, msg, args))\n    }\n  })\n  def('toHaveBeenCalledExactlyOnceWith', function (...args) {\n    const spy = getSpy(this)\n    const spyName = spy.getMockName()\n    const callCount = spy.mock.calls.length\n    const hasCallWithArgs = spy.mock.calls.some(callArg => equalsArgumentArray(callArg, args))\n    const pass = hasCallWithArgs && callCount === 1\n    const isNot = utils.flag(this, 'negate') as boolean\n\n    const msg = utils.getMessage(this, [\n      pass,\n      `expected \"${spyName}\" to be called once with arguments: #{exp}`,\n      `expected \"${spyName}\" to not be called once with arguments: #{exp}`,\n      args,\n    ])\n\n    if ((pass && isNot) || (!pass && !isNot)) {","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/expect/src/jest-expect.ts#L596-L632","documentation":"AssertionError thrown by toHaveBeenCalledWith/toBeCalledWith when the spy was never called with the exact arguments (positive form) or was called with them under .not. The message is enriched by formatCalls, which lists every received call with a diff against the expected arguments.","triggerScenarios":"expect(spy).toHaveBeenCalledWith(arg1, arg2) where no recorded call deep-equals (arg1, arg2); or the negated form where at least one call did match. Uses equalsArgumentArray (jestEquals element-by-element with iterableEquality).","commonSituations":"Argument shape mismatch — e.g. the code passed an object with extra keys, a string instead of a number, or a different array length. The diff in the message highlights the first divergence per call.","solutions":["Read the per-call diff in the failure output to find which argument differs.","If only a subset of arguments matters, wrap expected args with asymmetric matchers (expect.objectContaining, expect.any).","Confirm the spy captured the call you think it did — check spy.mock.calls in a debug step."],"exampleFix":"// before\nexpect(log).toHaveBeenCalledWith('user', 42);\n// after (relax shape)\nexpect(log).toHaveBeenCalledWith('user', expect.any(Number));","handlingStrategy":"validation","validationCode":"const recorded = spy.mock.calls;\nif (!recorded.some(c => c.length === args.length && c.every((v, i) => jestEquals(v, args[i])))) {\n  console.warn('no call matches expected args; recorded:', recorded);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Read the per-call diff in the failure to find the divergent argument.","Use asymmetric matchers (expect.any, expect.objectContaining) when only part of an argument is significant."],"tags":["assertion","mock","spy","tohavebeencalledwith","diff"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}