Cypress:封装获取元素href属性方法返回空值,如何正确处理?
问题:Cypress页面对象方法无法正确返回异步获取的元素属性值
问题重现
原本在测试用例中稳定运行的逻辑:获取元素hrefAppTheme的href属性,验证其是否属于字符串数组appThemes中的值:
describe('...',() => { it('should ...', () => { // ... let defaultAppTheme = ''; onMenuPage.hrefAppTheme.invoke('attr', 'href') .then(hrefVal => { if (typeof hrefVal !== 'undefined') { expect(hrefVal).to.be.oneOf(onMenuPage.appThemes); defaultAppTheme = hrefVal; // 后续引用defaultAppTheme变量... } }); }); });
为了复用逻辑,将其提取到页面对象类的getUserTheme方法中:
export default class MenuPage { // ... getUserTheme(): string { let userTheme = ''; cy.then(() => { this.hrefAppTheme.invoke('attr', 'href') .then(resVal => { if (typeof resVal !== 'undefined') { userTheme = resVal; } }); }); return userTheme; } }
调用时始终返回空字符串,断言失败:
describe('...',() => { it('should ...', () => { // ... let defaultAppTheme = ''; cy.wrap(onMenuPage.getUserTheme()).then(resVal => { defaultAppTheme = resVal; expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes); }); // 后续引用defaultAppTheme变量... }); });
错误信息:
AssertionError: expected '' to be one of [ 'theme-light.css', 'theme-dark.css' ] + expected - actual +[ 'theme-light.css', 'theme-dark.css' ]
问题根源
Cypress的所有命令都是异步执行的,getUserTheme方法中:
cy.then和invoke('attr', 'href')都是异步操作,会被放入Cypress的命令队列延后执行- 方法会立即返回初始值
'',等异步操作完成并赋值userTheme时,返回值已经被拿到,因此始终得到空字符串
解决方案
方案1:返回Cypress链式命令(推荐)
让页面对象方法直接返回Cypress的命令链,利用异步队列机制保证顺序,同时可封装断言逻辑:
export default class MenuPage { // ... getUserTheme() { // 返回Cypress.Chainable<string>,无需指定string类型 return this.hrefAppTheme.invoke('attr', 'href') .then(hrefVal => { if (typeof hrefVal !== 'undefined') { // 将断言封装到方法内,简化测试用例 expect(hrefVal).to.be.oneOf(this.appThemes); return hrefVal; } // 处理属性不存在的情况,可根据需求返回默认值或抛出错误 return ''; }); } }
调用方式:
describe('...',() => { it('should ...', () => { let defaultAppTheme = ''; onMenuPage.getUserTheme().then(hrefVal => { defaultAppTheme = hrefVal; // 后续对defaultAppTheme的操作必须放在此回调内,或继续链式调用保证顺序 }); }); });
方案2:使用async/await(需配置)
若习惯同步写法,可开启Cypress异步支持后使用async/await:
export default class MenuPage { // ... async getUserTheme(): Promise<string> { const hrefVal = await this.hrefAppTheme.invoke('attr', 'href'); if (typeof hrefVal !== 'undefined') { expect(hrefVal).to.be.oneOf(this.appThemes); return hrefVal; } return ''; } }
调用方式:
describe('...',() => { it('should ...', async () => { const defaultAppTheme = await onMenuPage.getUserTheme(); expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes); // 后续可直接引用defaultAppTheme }); });
更优封装建议
- 封装断言逻辑:将属性验证逻辑放入页面对象方法,测试用例只需关注业务流程,避免重复代码
- 处理边界情况:明确
href属性为undefined时的处理逻辑,比如返回默认主题或抛出错误,避免后续异常 - 遵循Cypress异步模型:优先使用链式命令而非
async/await,减少异步适配成本,更贴合框架设计
内容的提问来源于stack exchange,提问作者Jan and RESTless
相关产品推荐
相关产品推荐

