如何使用JavaScript、React和Cypress重构嵌套then代码并修复报错?
问题原因
- 首先排查变量名笔误:你代码上层定义的子家族变量名为
subfamily,但后续查询时使用的是subfamily1.name,如果实际代码里没有定义subfamily1变量,会导致查询参数错误,返回空数组。 - 核心偶现错误原因:和你之前封装
waitForItems的场景一致,API写入和查询存在数据延迟,你调用cy.apiGetSubFamily时没有等待数据同步完成,偶尔后台还没完成之前的写入操作就返回了空的查询结果,此时subfamily[0]为undefined,读取id就会报错。 - 额外问题:多层嵌套的then形成回调地狱,代码可读性差,中间变量全部挂载在外部作用域,容易出现变量污染和作用域问题。
修复方案
第一步:封装通用API等待重试函数
把你之前写的waitForItems逻辑抽象成通用函数,所有查询操作都可以复用,避免空结果问题:
export const waitForApiResult = (apiCall, predicate, retries = 5, interval = 1000) => { return cy.wrap(null).then(() => { return apiCall().then(result => { if (predicate(result) || retries <= 0) { expect(predicate(result)).to.be.true return result } cy.wait(interval) return waitForApiResult(apiCall, predicate, retries - 1, interval) }) }) }
第二步:用Cypress别名替代外部变量+扁平化链式调用
用Cypress内置的as()方法存储中间结果,不需要嵌套then,也不需要在外部定义变量存值,完全规避作用域问题。
重构后完整代码
// 通用重试函数复用原有waitForItems逻辑 export const waitForApiResult = (apiCall, predicate, retries = 5, interval = 1000) => { return cy.wrap(null).then(() => { return apiCall().then(result => { if (predicate(result) || retries <= 0) { expect(predicate(result)).to.be.true return result } cy.wait(interval) return waitForApiResult(apiCall, predicate, retries - 1, interval) }) }) } export const waitForItems = (retries, nrItems) => { return waitForApiResult( () => cy.apiGetItems(), items => items.length === nrItems, retries ) } const item = { name: 'name1' } const itemFamilyName = 'ItemFamily1' const subfamily1 = { name: 'subfamily1' } // 修正变量名和后续调用一致 describe('some test', () => { before(() => { cy.clickButtonByLabel('Click') switchUser(USERNAME) waitForItems(NUMBEROFRETRIES, NUMBEROFITEMS) // 获取item id并存储为别名 cy.apiGetItems({ name: item.name }) .its('0.id') .as('itemId') // 获取item family id cy.get('@itemId') .then(() => cy.apiGetItemFamily({ name: itemFamilyName })) .its('0.id') .as('familyId') // 加重试逻辑获取subfamily,从根源避免空结果报错 cy.get('@familyId') .then(familyId => { return waitForApiResult( () => cy.apiGetSubFamily({ name: subfamily1.name, item__family__id__in: familyId }), subfamily => subfamily.length > 0 ) }) .its('0.id') .as('subfamilyId') // 提交something cy.all(['@itemId', '@subfamilyId']) .then(([itemId, subfamilyId]) => { return cy.apiPostSomething({ ...something, items: [itemId], subfamily: [subfamilyId] }) }) .its('id') .as('somethingId') // 提交变量值 cy.get('@subfamilyId') .then(subfamilyId => cy.apiGetItemVariable({ key: ItemVariableName, subfamily__id__in: subfamilyId })) .its('0.id') .then(variableId => { return cy.apiPostVariableValue({ variable: variableId, value: controlValue }) }) // 最后执行patch操作 cy.all(['@somethingId']) .then(([somethingId]) => { return cy.apiGetsubFamily({ name: subfamily1.name }) }) .then(newControl => { const latestVersionControl = newControl[0].versions.find(v => v.version === '3.9.0') return cy.apiPatchSomething({ id: somethingId, controls: [latestVersionControl.id] }) }) }) })
注:如果你的Cypress版本没有内置
cy.all方法,可以用依次调用cy.get('@别名')的方式获取多个存储值,不会影响功能运行。
内容的提问来源于stack exchange,提问作者stackuser
相关产品推荐
相关产品推荐

