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

在Cypress中遍历数组并断言:API响应键值对匹配问题

问题描述

场景说明

我从某API调用得到如下响应数组:

[
    {
        "IsDatalakeEnabled": true,
        "RecoveryHr": {
            "IsRecoveryHREnabled": false,
            "RecoveryHeartRateInterval": 120,
            "RecoveryHeartRateSessionTime": 300
        }
    }
]

需要提取其中所有键值对,断言它们都存在于另一个API的响应体中,目标响应体结构片段如下:

...
"StudioAddress": null,
        "StudioProfileLanguage": {
            "LanguageName": "English",
            "LanguageCode": "en"
        },
        "IsDiversityChannel": true,
        "TotalDiversityRadios": 2,
        "IsDatalakeEnabled": false,
        "IsNetpulse": false,
        "RecoveryHeartRateInterval": 120,
        "RecoveryHeartRateSessionTime": 300,
        "IsRecoveryHREnabled": false,
        "StudioPhysicalLocationId": null,
        "StudioLocation": null,
        "IsIntroCapacityEnabled": false,
        "Status": null,
        "IsWeb": false,
        "OrangeBook": null,
        "IsFeReservationEnabled": true,
        "TimeZone": "America/New_York",
        "IsModifyMaxHr": false,
        "IsRunRowEnabled": false,
        "WeightMeasure": "LB",
...

遇到的问题

尝试用cy.each()遍历,但它把整个数组当成单个对象处理:

{
    "TotalDiversityRadios": "0",
    "IsDatalakeEnabled": true
}

也试过用_.castArray()把响应体转成数组,以及Object.entries(),都没解决问题。

2023年1月14日更新:测试代码及报错

我的测试代码如下:

validateStudioSettings() {
        cy.intercept('POST', '**/studio-settings').as('post')
        cy.contains('APPLY').should('be.enabled').click()
        cy.get('.dialog-content').should('be.visible').and('have.text', 'Are you sure you want to apply these settings?')
        cy.contains('AGREE').click()
        cy.get('.notification').should('be.visible').and('contain', 'Success').and('contain', 'Update studio settings successfully.')
        cy.wait('@post').then(post => {
            expect(post.response.statusCode).equal(200)
            expect(post.response.statusMessage).equal('OK')
            expect(post.response.body).equal('Update studio settings successfully.')
            let studios = _.castArray(post.request.body.StudioIds)
            let settings = _.castArray(post.request.body.Settings)//.map(([key, val]) => key + ': ' + val);
            console.log('Studios: ', studios)
            console.log('Settings: ', settings)
            cy.wrap(studios).each((studioId, index) => {
                console.log('StudioId: ', studioId, index)
                let idToken = localStorage.getItem("idToken")
                cy.request({
                    method: "GET",
                    url: `/` + studioId,
                    headers: {
                        'authorization-cognito': idToken
                    }
                }).then(response => {
                    cy.wrap(settings).each((setting) => {
                        console.log('Setting: ', setting)
                        cy.wrap(response).its('body').then(body => {
                            let el = _.castArray(body)
                            console.log('Body: ', el)
                            expect(el).to.contain(setting)
                        })
                    })
                })
            })
        })
    }

运行后Cypress测试器报错。

解决方案

核心问题分析

  1. 嵌套结构未展开:原始响应里的RecoveryHr是嵌套对象,直接遍历外层对象会漏掉内部的键值对;
  2. 断言方式错误:expect(el).to.contain(setting)是数组包含对象的逻辑,但目标响应是扁平对象,这种断言不适用;
  3. _.castArray()误用:目标响应body本身就是对象,转成数组后破坏了原有结构,导致断言失败。

修正步骤

1. 编写扁平化工具函数

先把带嵌套的设置对象转成扁平的键值对集合,确保所有字段都能被遍历到:

// 扁平化嵌套对象的工具函数
const flattenSettings = (obj) => {
  let result = {};
  for (const key in obj) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      Object.assign(result, flattenSettings(obj[key]));
    } else {
      result[key] = obj[key];
    }
  }
  return result;
}

2. 调整测试代码逻辑

替换原有的处理逻辑,用扁平化后的键值对逐一断言:

validateStudioSettings() {
  cy.intercept('POST', '**/studio-settings').as('post')
  cy.contains('APPLY').should('be.enabled').click()
  cy.get('.dialog-content').should('be.visible').and('have.text', 'Are you sure you want to apply these settings?')
  cy.contains('AGREE').click()
  cy.get('.notification').should('be.visible').and('contain', 'Success').and('contain', 'Update studio settings successfully.')
  
  cy.wait('@post').then(post => {
    expect(post.response.statusCode).equal(200)
    expect(post.response.statusMessage).equal('OK')
    expect(post.response.body).equal('Update studio settings successfully.')
    
    const studios = _.castArray(post.request.body.StudioIds)
    // 扁平化所有设置对象,处理嵌套结构
    const flattenedSettings = Object.assign({}, ...post.request.body.Settings.map(setting => flattenSettings(setting)))
    
    cy.wrap(studios).each(studioId => {
      const idToken = localStorage.getItem("idToken")
      cy.request({
        method: "GET",
        url: `/${studioId}`,
        headers: { 'authorization-cognito': idToken }
      }).then(response => {
        const responseBody = response.body
        // 遍历扁平化后的键值对,逐一验证存在且值匹配
        Object.entries(flattenedSettings).forEach(([key, value]) => {
          expect(responseBody).to.have.property(key, value)
        })
      })
    })
  })
}

3. 关键修正点说明

  • 扁平化嵌套对象:通过工具函数把RecoveryHr内部的键值对提取到顶层,确保所有需要断言的字段都被覆盖;
  • 正确断言逻辑:使用expect(responseBody).to.have.property(key, value)直接验证目标响应体包含指定键且值一致,这是针对对象的标准断言方式;
  • 移除无效转换:不再将目标响应body转成数组,直接使用原始对象结构进行断言。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:40:42