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

Cypress测试批量写入数据库仅最后一条生效,求正确异步处理方案

Cypress多产品创建仅最后一个成功的问题解决

问题现象

编写Cypress测试时,尝试向数据库写入多个产品,但仅最后一个产品成功写入数据库。

相关代码

测试代码(my_test.cy.js)

// my_test.cy.js
import CreateProductPage from "../pages/CreateProductPage";

describe('product detail page', () => {
    beforeEach(() => {
        cy.login('admin', 'shop')
    })

    it('should print typed product', () => {
        cy.createProduct(null, 'Component Product 0', '0')
        CreateProductPage.elements.productDetailTabs().should('exist') // 用于检测实体是否写入
        cy.createProduct('combinable', 'Combined Test Product', '0')
        CreateProductPage.elements.productDetailTabs().should('exist') // 用于检测实体是否写入
    })
})

自定义命令(commands.js)

// commands.js
Cypress.Commands.add('createProduct', (type, name, grossPrice) => {
    cy.visit('/#/sw/product/create')
    CreateProductPage.elements.productDetailTabs().should('not.exist').then(() => {
        if(type === 'combinable') {
            CreateProductPage.elements.radioBtnCombinableProduct().click()
        }
        CreateProductPage.elements.inputProductName().clear().type(name)
        CreateProductPage.elements.inputPriceFieldGross().type(grossPrice)
        SwPageHeader.elements.btnProductSave().click()
    })
})

临时解决代码(添加cy.wait)

// my_test.cy.js
describe('product detail page', () => {
    beforeEach(() => {
        cy.login('admin', 'shop')
    })

    it('should print typed product', () => {
        cy.createProduct(null, 'Component Product 0', '0')
        CreateProductPage.elements.productDetailTabs().should('exist')
        cy.wait(300)        
        cy.createProduct('combinable', 'Combined Test Product', '0')
        CreateProductPage.elements.productDetailTabs().should('exist')
    })
})

页面元素定义(CreateProductPage.js)

// pages/CreateProductPage.js

class CreateProductPage {
    elements = {
        productDetailTabs: () => cy.get('div.sw-product-detail-page__tabs'),
        radioBtnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-0'),
        radioBtnUnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-1'),
        inputProductName: () => cy.get('input#sw-field--product-name'),
        inputPriceFieldGross: () => cy.get('div.sw-list-price-field__price input#sw-price-field-gross'),
    }
}

module.exports = new CreateProductPage();

疑问

  1. 该问题是否由Cypress的异步特性导致?
  2. 若是,如何正确处理该异步问题?使用then()链式调用后问题依旧,添加cy.wait(300)可临时解决,但想寻求正确的实现方式。

解答

1. 问题根源

确实和Cypress的异步特性相关,但核心原因是未正确等待第一个产品的创建流程完全结束,就启动了第二个产品的创建操作。

原自定义命令createProduct中,点击保存按钮后没有等待保存完成(包括API请求结束、页面跳转完成)就结束了命令,导致测试代码提前执行第二个createProduct的cy.visit,覆盖了第一个产品的保存请求或打乱了页面状态,最终只有最后一个产品的创建流程完整执行。

2. 正确处理方式

要让每个产品创建的异步流程完全结束后,再执行下一个操作,最可靠的方式是在自定义命令内部完成所有必要的等待逻辑,确保命令执行完毕时,产品已经成功写入数据库且页面状态稳定。

方案一:等待页面状态稳定

修改自定义命令,在点击保存后等待产品详情页的标签栏(productDetailTabs)出现,确保页面跳转完成、产品创建成功:

// commands.js
Cypress.Commands.add('createProduct', (type, name, grossPrice) => {
    cy.visit('/#/sw/product/create')
    // 等待创建页面加载完成(详情标签栏不存在)
    CreateProductPage.elements.productDetailTabs().should('not.exist')
    
    if(type === 'combinable') {
        CreateProductPage.elements.radioBtnCombinableProduct().click()
    }
    CreateProductPage.elements.inputProductName().clear().type(name)
    CreateProductPage.elements.inputPriceFieldGross().type(grossPrice)
    SwPageHeader.elements.btnProductSave().click()
    
    // 等待保存完成,详情标签栏出现,确保产品创建成功
    CreateProductPage.elements.productDetailTabs().should('exist')
})

对应的测试代码可以简化,无需额外断言:

// my_test.cy.js
import CreateProductPage from "../pages/CreateProductPage";

describe('product detail page', () => {
    beforeEach(() => {
        cy.login('admin', 'shop')
    })

    it('should print typed product', () => {
        cy.createProduct(null, 'Component Product 0', '0')
        cy.createProduct('combinable', 'Combined Test Product', '0')
    })
})

方案二:结合API拦截(更可靠)

如果页面元素的判断不够稳定,可以拦截产品保存的API请求,等待请求成功后再确认页面状态,双重保障:

// commands.js
Cypress.Commands.add('createProduct', (type, name, grossPrice) => {
    // 拦截产品保存的POST请求,根据实际API路径调整
    cy.intercept('POST', '/api/product').as('saveProduct')
    
    cy.visit('/#/sw/product/create')
    CreateProductPage.elements.productDetailTabs().should('not.exist')
    
    if(type === 'combinable') {
        CreateProductPage.elements.radioBtnCombinableProduct().click()
    }
    CreateProductPage.elements.inputProductName().clear().type(name)
    CreateProductPage.elements.inputPriceFieldGross().type(grossPrice)
    SwPageHeader.elements.btnProductSave().click()
    
    // 等待API请求完成,确保数据写入数据库
    cy.wait('@saveProduct').its('response.statusCode').should('eq', 200)
    // 再等待页面跳转完成
    CreateProductPage.elements.productDetailTabs().should('exist')
})

为什么之前的then()无效?

原代码中then()只是包裹了表单填写和保存操作,但没有在then()内部等待保存完成的状态,命令依然会在点击保存后立即结束,无法阻止后续测试代码提前执行。


内容的提问来源于stack exchange,提问作者rammi22

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 04:55:13