Spying on promisify'd functions broken in 3.2.0
Describe the bug
After upgrading to 3.2.0+ Vitest no longer mocks functions which are wrapped in util.promisify(). Still works properly with Vitest 3.1.4
Reproduction
See StackBlitz link:
https://stackblitz.com/edit/vitest-dev-vitest-5mywuynf?file=test%2Fbasic.test.ts
Note that how the very basic test fails with:
❯ test/basic.test.ts (1 test | 1 failed) 121ms
× exec does the right thing 120ms
→ expected "exec" to be called with arguments: [ 'echo "hi"', Any<Function> ]
If you rollback Vitest to 3.1.4, reinstall and re-run the same tests. It will pass
The full code for reproduction is:
// basic.tsx
import util from 'node:util';
import child_process from 'node:child_process';
const myFunction = () => {
const exec = util.promisify(child_process.exec);
return exec('echo "hi"');
};
export default myFunction;
// basic.test.tsx
import { expect, test, vi } from 'vitest';
import child_process from 'node:child_process';
import myFunction from './basic';
test('exec does the right thing', async () => {
const execSpy = vi
.spyOn(child_process, 'exec')
.mockImplementation((_, callback) => {
callback(null, 'success!');
});
await myFunction();
expect(execSpy).toHaveBeenCalledWith('echo "hi"', expect.any(Function));
});
System Info
System:
OS: macOS 15.5
CPU: (12) arm64 Apple M2 Max
Memory: 112.34 MB / 32.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.18.3 - ~/.nvm/versions/node/v20.18.3/bin/node
Yarn: 1.22.22 - ~/.yarn/bin/yarn
npm: 10.8.2 - ~/.nvm/versions/node/v20.18.3/bin/npm
bun: 1.2.15 - ~/.bun/bin/bun
Browsers:
Chrome: 137.0.7151.69
Chrome Canary: 139.0.7228.0
Safari: 18.5
Used Package Manager
npm
Validations
- [x] Follow our Code of Conduct
- [x] Read the Contributing Guidelines.
- [x] Read the docs.
- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [x] The provided reproduction is a minimal reproducible example of the bug.
Interestingly, I think this is somewhat expected. vi.spyOn now copies over all static properties, and child_process.exec has a custom Symbol.for('nodejs.util.promisify.custom') which is actually called instead of the child_process.exec directly.
This property can't be overwritten or deleted, so it's impossible to spy now. Maybe we need to add a flag to allow/disallow copying static properties?
This property can't be overwritten or deleted, so it's impossible to spy now. Maybe we need to add a flag to allow/disallow copying static properties?
We can also always ignore Symbol.for('nodejs.util.promisify.custom').
Thanks @sheremet-va . Just confirming that 4.0.0-beta.7 fixes this issue.