nodejs / nodejs/node

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

オープン
#58,161 コメント 9 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

test_runner
主要言語
JavaScript
スター
122k
フォーク
37.3k
平均マージ
4日 2時間
マージ済み PR(30日)
283

説明

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)

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

node:test MockFunctionContext の動作から始め、node --test を使って callback-test.js で問題を再現します。コールバックベースの fs.writeFile モックの経路を追跡し、コールバックテストが呼び出し回数 1 を報告するようにしてから、コールバック版と promisified 版の両方の例を再実行して、期待される結果を確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, node.js
領域
testing
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。