{"id":14930,"library":"spy4js","title":"Spy4js Testing Spy Framework","description":"Spy4js is a standalone JavaScript and TypeScript testing spy framework designed for integration with test runners like Vitest and Jest. It provides a robust API for creating and managing spies, focusing on test readability, detailed error messages, and efficient serialization of call arguments. The package aims to offer an intuitive alternative or supplement to the built-in spying capabilities of popular test frameworks. The current stable version is 5.0.0, released in September 2025. While release cadence can be irregular, significant updates (like the TypeScript migration in v3.0.0) introduce notable breaking changes. Key differentiators include an API optimized for readability, enhanced error reporting with detailed comparisons, and features like customizable behavior and module mocking capabilities for both CommonJS and ES Modules.","status":"active","version":"5.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/fdc-viktor-luft/spy4js","tags":["javascript","spy","test","jest","vitest","TypeScript","typescript"],"install":[{"cmd":"npm install spy4js","lang":"bash","label":"npm"},{"cmd":"yarn add spy4js","lang":"bash","label":"yarn"},{"cmd":"pnpm add spy4js","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Spy4js is primarily an ESM-first library since v3.0.0, although CJS bundles may exist. For TypeScript and modern environments, use the `import` statement. Since v3.0.0, `Spy` is a callable object, not a class, so omit the `new` keyword.","wrong":"const Spy = require('spy4js');","symbol":"Spy","correct":"import { Spy } from 'spy4js';"},{"note":"Static methods like `Spy.on` are called directly on the `Spy` export. Ensure `Spy` is imported as a named export.","wrong":"const spy = new Spy.on(someObject, 'methodName');","symbol":"Spy.on","correct":"import { Spy } from 'spy4js'; const spy = Spy.on(someObject, 'methodName');"},{"note":"When mocking module exports, use `await import()` for ES Modules and `require()` for CommonJS contexts. `Spy.mock` is a static method and not a constructor.","wrong":"const moduleMocks = Spy.mock(require('./my-module'), 'useMe');","symbol":"Spy.mock","correct":"import { Spy } from 'spy4js'; const moduleMocks = Spy.mock(await import('./my-module'), 'useMe');"}],"quickstart":{"code":"import { Spy } from 'spy4js';\n\n// Initialize a basic spy\nconst mySpy = Spy('myFunctionSpy');\n\n// Simulate calling the spy\nmySpy(1, 2, 'hello');\nmySpy({ data: 'test' });\n\n// Assertions\nif (!mySpy.wasCalled()) {\n  throw new Error('mySpy was not called!');\n}\n\nif (!mySpy.wasCalledWith(1, 2, 'hello')) {\n  throw new Error('mySpy was not called with expected arguments!');\n}\n\nif (mySpy.getCallCount() !== 2) {\n  throw new Error(`Expected 2 calls, got ${mySpy.getCallCount()}`);\n}\n\nconsole.log('Spy calls:', mySpy.getAllCallArguments());\n\n// Mock an existing object's method\nconst service = { \n  getData: (id: string) => `Data for ${id}`,\n  process: () => 'done'\n};\nconst getDataSpy = Spy.on(service, 'getData').returns('Mocked Data');\n\nconsole.log(service.getData('123')); // Outputs: Mocked Data\nif (!getDataSpy.wasCalledWith('123')) {\n  throw new Error('getDataSpy was not called as expected!');\n}\n\nconsole.log('All good with spy4js!');","lang":"typescript","description":"Demonstrates initializing a spy, simulating calls, and performing basic assertions like checking call count and arguments, and mocking an existing object's method."},"warnings":[{"fix":"Remove the `new` keyword when creating a spy instance. Change `new Spy()` to `Spy()`.","message":"Starting from v3.0.0, the main `Spy` export is no longer a class and cannot be instantiated with the `new` keyword. It is now a callable object.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"To check if a spy was not called at all, use `spy.wasNotCalled()`. If you need to check for specific call arguments, provide them to `spy.hasCallHistory(...)`.","message":"As of v3.1.0, calling `spy.hasCallHistory()` without any arguments will always fail. This method now requires arguments to check for specific call history.","severity":"breaking","affected_versions":">=3.1.0"},{"fix":"Evaluate `spy4js` based on its specific benefits (readability, detailed error messages, unique features) rather than as a fundamental dependency for spying. It's intended as an alternative or complementary tool.","message":"The `spy4js` library explicitly states that it is not strictly necessary as most modern test frameworks (like Jest and Vitest) already include their own spying capabilities.","severity":"gotcha","affected_versions":"*"},{"fix":"Be aware of this configuration option when debugging test failures related to call order. If enabling it, ensure your tests explicitly account for the exact sequence of expected calls.","message":"Version 3.1.0 introduced an 'enforce-order mode' which can be enabled via `Spy.configure({ enforceOrder: true })`. Enabling this significantly changes how spy calls are matched and validated, requiring calls to happen in a specific sequence.","severity":"gotcha","affected_versions":">=3.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Remove the `new` keyword. Instantiate the spy by calling `Spy()` directly, e.g., `const mySpy = Spy();`.","cause":"Attempting to create a spy instance using `new Spy()` after upgrading to spy4js v3.0.0 or higher.","error":"TypeError: Spy is not a constructor"},{"fix":"If you intend to check if the spy was never called, use `spy.wasNotCalled()`. If you want to check for specific call patterns, provide arguments to `spy.hasCallHistory(...)`.","cause":"Invoking `spy.hasCallHistory()` without any arguments, which is deprecated and causes an error since spy4js v3.1.0.","error":"Error: spy.hasCallHistory() must be called with arguments or use wasNotCalled()"},{"fix":"Ensure that your module mocking strategy aligns with your project's module system. For ES Modules (e.g., in Vitest), use `await import()` with `vi.mock()`. For CommonJS (e.g., in Jest without ESM enabled), use `require()`.","cause":"Attempting to use `require()` for module mocking with `Spy.mock` in an ES Module context, or vice-versa.","error":"ERR_REQUIRE_ESM (or similar module resolution error)"}],"ecosystem":"npm"}