nodejs / nodejs/node

`MockFunctionContext` cannot be used to track callback / continuation-passing methods

Open
#58,161 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

test_runner
Dominant language
JavaScript
Stars
122k
Forks
37.3k
Avg merge
4d 2h
Merged PRs (30d)
283

Description

Version

v22.14.0

Platform
Darwin MacBookPro 24.4.0 Darwin Kernel Version 24.4.0: Fri Apr 11 18:28:23 PDT 2025; root:xnu-11417.101.15~117/RELEASE_X86_64 x86_64
Subsystem

node:test

What steps will reproduce the bug?

Consider the following test:

let fs = require('node:fs')

function writer (cb) {
  console.log('calling fs.writeFile')
  fs.writeFile('test.test', 'hi', cb)
}

let { test } = require('node:test')
test('callback test mocking not working?', (t, done) => {
  console.log('mocking fs.writeFile')
  t.mock.method(fs, 'writeFile', (dest, data, cb) => {
    console.log('mock writeFile executing, calling back')
    cb()
  })
  writer(err => {
    if (err) console.warn('got a writeFile error', err)
    console.log('fs.writeFile mock callcount:', fs.writeFile.mock.callCount())
    done()
  })
})

When I run the above test, the call count on the mocked method is 0, even though the console.log from within the mocked method is output:

➜ node --test callback-test.js
mocking fs.writeFile
calling fs.writeFile
mock writeFile executing, calling back
fs.writeFile mock callcount: 0
✔ callback test mocking not working? (1.868146ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 79.29258
How often does it reproduce? Is there a required condition?

Every time.

What is the expected behavior? Why is that the expected behavior?

I would expect that the call count for the mocked method is 1 in the above test. If the test method provides an optional done callback parameter, that signaled to me that node:test could be used to test continuation-passing style of source code.

What do you see instead?

Instead, call count is 0:

➜ node --test callback-test.js
mocking fs.writeFile
calling fs.writeFile
mock writeFile executing, calling back
fs.writeFile mock callcount: 0
✔ callback test mocking not working? (1.868146ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 79.29258
Additional information

I am in the process of updating old modules - several years old and that have worked just fine since about 2017 - and removing third party test frameworks and moving to the native node test runner module (node:test). These modules are written in this continuation-passing style - functions that accept a callback.

I suppose the entire chain of callbacks (not sure what term to use for this) must complete before the mock method records a call? The event loop must complete a lap before the method tracking is registered? If I extend the test in my example with a promisified version, then my expectations are met:

let fs = require('node:fs')
let { promisify } = require('node:util')

function writer (cb) {
  console.log('calling fs.writeFile')
  fs.writeFile('test.test', 'hi', cb)
}
let promisifiedWriter = promisify(writer)

let { test } = require('node:test')
test('callback test mocking not working?', (t, done) => {
  console.log('mocking fs.writeFile')
  t.mock.method(fs, 'writeFile', (dest, data, cb) => {
    console.log('mock writeFile executing, calling back')
    cb()
  })
  writer(err => {
    if (err) console.warn('got a writeFile error', err)
    console.log('fs.writeFile mock callcount:', fs.writeFile.mock.callCount())
    done()
  })
})
test('promisified test mocking working', async (t) => {
  console.log('mocking fs.writeFile')
  t.mock.method(fs, 'writeFile', (dest, data, cb) => {
    console.log('mock writeFile executing, calling back')
    cb()
  })
  await promisifiedWriter()
  console.log('fs.writeFile mock callcount:', fs.writeFile.mock.callCount())
})
➜ node --test callback-test.js
mocking fs.writeFile
calling fs.writeFile
mock writeFile executing, calling back
fs.writeFile mock callcount: 0
mocking fs.writeFile
calling fs.writeFile
mock writeFile executing, calling back
fs.writeFile mock callcount: 1
✔ callback test mocking not working? (1.858847ms)
✔ promisified test mocking working (0.314972ms)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the node:test MockFunctionContext behavior and reproduce the issue using callback-test.js with node --test. Trace the callback-based fs.writeFile mock path and make the callback test report a call count of 1, then rerun both the callback and promisified examples to confirm their expected results.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.