Cypress测试SAP Hybris促销多用例空白页问题及优化咨询
SAP Hybris促销测试:多测试用例执行后空白页阻塞问题优化方案
问题背景
我用Cypress编写了SAP Hybris后台的促销项测试脚本,单个测试用例执行完全正常,但添加多个促销对应的测试用例后,第一个用例执行完成,第二个用例启动就会阻塞——页面变成空白,无法找到后续用例所需的元素。
目前临时解决方案是把登录、角色选择等初始化步骤移到beforeEach钩子中,让每个测试用例重复执行这些步骤,但这样会导致测试效率极低,希望找到无需重复初始化的优化方案。
原测试代码
Cypress.on('uncaught:exception', (err, runnable) => { return false; }); describe('Promotions test', () => { const loginPageUrl = 'https://backoffice.stg.iqos.com/backoffice/login.zul'; const username = 'USERNAME HERE'; const password = 'PASSWORD HERE'; const promotionEditorRole = 'Promotion Editor'; const proceedButtonText = 'PROCEED'; const marketingMenuText = 'Marketing'; const promotionRulesText = 'Promotion Rules'; before(() => { // Login process that runs once before all test cases cy.visit(loginPageUrl, { failOnStatusCode: false }).wait(1000); cy.get('input[placeholder="Enter user name"]:visible') .type(username, { force: true }).wait(300); cy.get('input[placeholder="Enter password"]:visible') .type(password, { force: true }); cy.get('.login_btn').click({ force: true }).wait(30000); // Select Promotion Editor Role cy.get('.z-listcell-content').contains(promotionEditorRole) .should('exist').click({ force: true }).wait(2000); cy.get('.yw-selector-btn').contains(proceedButtonText) .should('exist').click({ force: true }).wait(15000); }); beforeEach(() => { // Navigation process before each test case cy.get('.yw-navigationNode-level1').contains(marketingMenuText) .should('exist').click({ force: true }).wait(3000); cy.get('span.z-label').contains(promotionRulesText) .should('exist').click({ force: true }).wait(5000); }); it('Check Promotion 1', () => { const promotionCode = 'CR_PMI_MGM_Godfather_Discount_01_More'; const promotionName = 'MGM GODFATHER Reward'; const websiteEnv = 'Costa Rica_Promotion_Group'; const promotionVersion = '4'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', 'CR_PMI_MGM_Godfather_Discount_01_More'); cy.wait(10000); }); it('Check Promotion 2', () => { const promotionCode = 'UK_15_Off_First_Order'; const promotionName = '15% off first ZYN Order'; const websiteEnv = 'PUBLISHED'; const promotionVersion = '0'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', 'UK_15_Off_First_Order'); }); it('Check Promotion 3', () => { const promotionCode = 'UK_ZYN_Free_Sample'; const promotionName = 'ZYN Free Sample'; const websiteEnv = 'PUBLISHED'; const promotionVersion = '0'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', 'UK_ZYN_Free_Sample'); }); }); function searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion) { cy.get('.z-bandbox-input') .eq(6) .should('exist') .clear() .type(promotionCode, { force: true }) .wait(300); cy.get('.yw-textsearch-searchbutton.z-button') .eq(1) .click({ force: true }) .wait(10000); cy.log("Promotion Code: " + promotionCode); cy.log("Promotion Name: " + promotionName); cy.log("Website Environment: " + websiteEnv); cy.log("Promotion Version: " + promotionVersion); const selector = "tr.yw-coll-browser-hyperlink:has(.yw-listview-cell-label.z-label:contains(\"" + promotionCode + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionName + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + websiteEnv + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionVersion + "\"))"; function checkPage() { cy.get('body').then($body => { if ($body.find(selector).length > 0) { cy.get(selector).within(() => { cy.get(".yw-listview-cell-label.z-label:contains(\"" + promotionVersion + "\")") .click({ force: true }) .wait(10000); }); } else { cy.get('button.z-paging-button.z-paging-next[title="Next Page"]').eq(2).then($button => { cy.log('Button found:', $button.length > 0 ? 'Yes' : 'No'); cy.log('Button visibility:', $button.is(':visible') ? 'Visible' : 'Not Visible'); cy.log('Button disabled attribute:', $button.attr('disabled') ? 'Disabled' : 'Enabled'); if ($button.is(':visible') && !$button.attr('disabled')) { cy.log('Clicking the next page button'); cy.wrap($button).click({ force: true }).wait(5000); checkPage(); } else { cy.log('Promotion not found or next page button is not clickable'); } }); } }); } checkPage(); }
问题根源分析
- Cypress默认状态重置:Cypress在每个测试用例结束后会自动清除cookie、localStorage等会话数据,导致Hybris后台会话失效,页面跳转到空白登录页。
- 页面上下文破坏:前一个测试用例打开了促销详情页,没有回到列表页,导致下一个用例启动时页面处于非预期状态。
- 硬等待依赖:代码中大量使用
wait(xxx)硬等待,容易导致页面加载未完成就执行后续操作,引发状态异常。
优化方案
1. 保留单次登录,会话持久化
在before钩子中完成一次登录后,用Cypress.Cookies.preserveOnce()保留会话关键cookie,避免后续用例重复登录:
before(() => { // 登录步骤... // 保留会话cookie(替换为Hybris实际使用的会话cookie名,通常是JSESSIONID) Cypress.Cookies.preserveOnce('JSESSIONID'); });
2. 用afterEach重置页面状态
每个测试用例结束后,强制回到促销规则列表页,确保下一个用例的起始环境一致:
afterEach(() => { // 关闭当前打开的促销详情标签页(如果存在) cy.get('.z-tab-close').click({ force: true, multiple: true }).wait(2000); // 重新导航到促销规则列表页 cy.get('.yw-navigationNode-level1').contains(marketingMenuText) .should('exist').click({ force: true }); cy.get('span.z-label').contains(promotionRulesText) .should('exist').click({ force: true }); // 等待列表页加载完成,用断言代替硬等待 cy.get('.z-bandbox-input').eq(6).should('exist'); });
3. 替换硬等待为断言等待
把代码中所有wait(xxx)替换为基于元素存在/可见的断言,Cypress会自动等待元素加载完成,提升稳定性:
// 替换登录后的硬等待 // cy.wait(30000); cy.get('.yw-navigationNode-level1').contains(marketingMenuText).should('exist'); // 替换搜索后的硬等待 // cy.wait(10000); cy.get('.yw-coll-browser-hyperlink').should('exist');
4. 优化测试用例内的状态清理
在每个测试用例的最后,主动关闭当前打开的促销详情标签,避免影响下一个用例:
it('Check Promotion 1', () => { // ...测试步骤 // 关闭促销详情标签 cy.get('.z-tab-close').click({ force: true }); });
优化后完整代码示例
Cypress.on('uncaught:exception', (err, runnable) => { return false; }); describe('Promotions test', () => { const loginPageUrl = 'https://backoffice.stg.iqos.com/backoffice/login.zul'; const username = 'USERNAME HERE'; const password = 'PASSWORD HERE'; const promotionEditorRole = 'Promotion Editor'; const proceedButtonText = 'PROCEED'; const marketingMenuText = 'Marketing'; const promotionRulesText = 'Promotion Rules'; before(() => { // 单次登录 cy.visit(loginPageUrl, { failOnStatusCode: false }); cy.get('input[placeholder="Enter user name"]:visible') .type(username, { force: true }); cy.get('input[placeholder="Enter password"]:visible') .type(password, { force: true }); cy.get('.login_btn').click({ force: true }); // 等待登录完成,用断言替代硬等待 cy.get('.z-listcell-content').contains(promotionEditorRole).should('exist'); // 选择角色 cy.get('.z-listcell-content').contains(promotionEditorRole).click({ force: true }); cy.get('.yw-selector-btn').contains(proceedButtonText).click({ force: true }); // 等待后台加载完成 cy.get('.yw-navigationNode-level1').contains(marketingMenuText).should('exist'); // 保留会话cookie Cypress.Cookies.preserveOnce('JSESSIONID'); }); beforeEach(() => { // 导航到促销规则列表页 cy.get('.yw-navigationNode-level1').contains(marketingMenuText).click({ force: true }); cy.get('span.z-label').contains(promotionRulesText).click({ force: true }); cy.get('.z-bandbox-input').eq(6).should('exist'); }); afterEach(() => { // 关闭所有打开的标签页,回到列表 cy.get('.z-tab-close').click({ force: true, multiple: true }).catch(() => {}); cy.get('.yw-navigationNode-level1').contains(marketingMenuText).click({ force: true }); cy.get('span.z-label').contains(promotionRulesText).click({ force: true }); cy.get('.z-bandbox-input').eq(6).should('exist'); }); it('Check Promotion 1', () => { const promotionCode = 'CR_PMI_MGM_Godfather_Discount_01_More'; const promotionName = 'MGM GODFATHER Reward'; const websiteEnv = 'Costa Rica_Promotion_Group'; const promotionVersion = '4'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', promotionCode); }); it('Check Promotion 2', () => { const promotionCode = 'UK_15_Off_First_Order'; const promotionName = '15% off first ZYN Order'; const websiteEnv = 'PUBLISHED'; const promotionVersion = '0'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', promotionCode); }); it('Check Promotion 3', () => { const promotionCode = 'UK_ZYN_Free_Sample'; const promotionName = 'ZYN Free Sample'; const websiteEnv = 'PUBLISHED'; const promotionVersion = '0'; searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion); cy.get('.z-tab-content') .contains('Rule Properties') .scrollIntoView() .click({ force: true }); cy.get('.ye-input-text.ye-com_hybris_cockpitng_editor_defaulttext.z-textbox.z-textbox-readonly') .should('have.value', promotionCode); }); }); function searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion) { cy.get('.z-bandbox-input') .eq(6) .should('exist') .clear() .type(promotionCode, { force: true }); cy.get('.yw-textsearch-searchbutton.z-button') .eq(1) .click({ force: true }); cy.log(`Promotion Code: ${promotionCode}`); cy.log(`Promotion Name: ${promotionName}`); cy.log(`Website Environment: ${websiteEnv}`); cy.log(`Promotion Version: ${promotionVersion}`); const selector = `tr.yw-coll-browser-hyperlink:has(.yw-listview-cell-label.z-label:contains("${promotionCode}")):has(.yw-listview-cell-label.z-label:contains("${promotionName}")):has(.yw-listview-cell-label.z-label:contains("${websiteEnv}")):has(.yw-listview-cell-label.z-label:contains("${promotionVersion}"))`; function checkPage() { cy.get('body').then($body => { if ($body.find(selector).length > 0) { cy.get(selector).within(() => { cy.get(`.yw-listview-cell-label.z-label:contains("${promotionVersion}")`) .click({ force: true }); }); // 等待详情页加载 cy.get('.z-tab-content').contains('Rule Properties').should('exist'); } else { cy.get('button.z-paging-button.z-paging-next[title="Next Page"]').eq(2).then($button => { cy.log('Button found:', $button.length > 0 ? 'Yes' : 'No'); cy.log('Button visibility:', $button.is(':visible') ? 'Visible' : 'Not Visible'); cy.log('Button disabled attribute:', $button.attr('disabled') ? 'Disabled' : 'Enabled'); if ($button.is(':visible') && !$button.attr('disabled')) { cy.log('Clicking the next page button'); cy.wrap($button).click({ force: true }); checkPage(); } else { cy.log('Promotion not found or next page button is not clickable'); } }); } }); } checkPage(); }
内容的提问来源于stack exchange,提问作者No Tools No Craft
相关产品推荐
相关产品推荐

