test(miniapp): 行为契约的桩改为忠实模拟 wx 回调包装(异常不外逸,转 App.onError)
原桩直接同步调用 `opt.success(...)`,被测代码在回调内抛错时异常会**外逸到测试进程**, 于是「请求永不 settle」这一真实后果反而测不出来 —— 报出来的是注入的异常本身, 而不是「Promise 永久 pending」。 真实小程序里 `wx.request` 的回调是被 wx 包装的:回调内抛错**不会**打断请求链, 而是被捕获后走 `App.onError`(本轮控制台实测 `[App onError] MiniProgramError`), 后果是 `resolve` 永不执行。桩必须保真这一点,否则判据会在错误的地方变红。 改动: - `respond()` 捕获回调内异常并记录,不外逸; - 成功路径新增断言:成功回调内**不得**抛出任何异常(附明确失败信息)。 负向自证(改前已实测):在 `resolve(body.data)` 之前注入一行抛错 → 本契约输出 `Promise 在 1000ms 内未 settle —— 收尾逻辑又跑到了 resolve 之前` 并退出 1; 移除注入后 `git diff --exit-code` 为空(逐字节还原),全量门禁 **18/18 PASS**。
This commit is contained in:
@@ -37,11 +37,22 @@ globalThis.wx = {
|
||||
|
||||
const api = require(resolve(root, 'miniapp/utils/api.js'))
|
||||
|
||||
/**
|
||||
* 模拟 wx 的回调包装:回调内抛出的异常**不会外逸**,而是被 wx 捕获后走 `App.onError`
|
||||
* (本机控制台实测即如此:`[App onError] MiniProgramError ...`)。
|
||||
* 保真这一点很关键 —— 否则桩会把异常直接抛给测试进程,
|
||||
* 于是"请求永不 settle"这一真实后果反而测不出来。
|
||||
*/
|
||||
const callbackErrors = []
|
||||
function respond(statusCode, data) {
|
||||
assert.ok(pendingRequest, '没有待响应的请求 —— 被测代码没走到 wx.request')
|
||||
const opt = pendingRequest
|
||||
pendingRequest = null
|
||||
opt.success({ statusCode, data })
|
||||
try {
|
||||
opt.success({ statusCode, data })
|
||||
} catch (error) {
|
||||
callbackErrors.push(error)
|
||||
}
|
||||
}
|
||||
|
||||
/** 断言 Promise 在限定时间内 settle,否则视为「永不 settle」缺陷 */
|
||||
@@ -69,6 +80,13 @@ function withinSettle(promise, ms = 1000) {
|
||||
key,
|
||||
'幂等键必须真的写进请求头(否则后端无法识别重复投递)',
|
||||
)
|
||||
// 成功回调内**不得**抛出任何异常:一旦抛出,wx 会捕获后转 App.onError,
|
||||
// 而 `resolve` 已经不会执行 → 该请求的 Promise 永久 pending(页面卡 loading)。
|
||||
assert.deepEqual(
|
||||
callbackErrors.map((e) => `${e && e.name}: ${e && e.message}`),
|
||||
[],
|
||||
'成功回调内抛出了异常 —— 在真实小程序里会表现为该请求永不 settle(页面卡 loading)',
|
||||
)
|
||||
}
|
||||
|
||||
// ── 2. 成功之后必须释放幂等键:同域下一次操作应拿到**新**键 ──
|
||||
|
||||
Reference in New Issue
Block a user