You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }

问题根源分析

  1. Cypress默认状态重置:Cypress在每个测试用例结束后会自动清除cookie、localStorage等会话数据,导致Hybris后台会话失效,页面跳转到空白登录页。
  2. 页面上下文破坏:前一个测试用例打开了促销详情页,没有回到列表页,导致下一个用例启动时页面处于非预期状态。
  3. 硬等待依赖:代码中大量使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 21:05:55