
在 cypress 测试中,直接在异步回调函数外部访问变量常导致 '未定义' 错误。本文将详细讲解如何利用 cypress 的别名(alias)机制,从服务器响应中捕获并封装复杂数据对象。通过 `cy.wrap().as()` 创建别名,再使用 `cy.get().then()` 安全地在后续测试步骤中重用这些数据,从而有效管理测试状态,提升测试代码的健壮性和可维护性。
在 Cypress 端到端测试中,经常需要从前一个步骤(例如 API 响应)中提取数据,并在后续步骤中重用。然而,由于 Cypress 命令的异步性质和其独特的命令队列机制,直接在 cy.then() 回调函数内部定义变量并尝试在外部访问,通常会导致 ReferenceError: [变量名] is not defined 的错误。这正是许多初学者在尝试将响应数据保存为普通 JavaScript 对象并在测试中后续步骤重用时遇到的常见挑战。
考虑以下场景,我们希望从服务器响应中解析出一个商品对象 Item,并在后续的 cy.addItemToShoppingCart 命令中使用它:
cy.wait('@pageLoaded').then(({ response }) => {
expect(response.statusCode).to.equal(200);
let i = randomItem();
// 在回调函数内部创建对象
const Item = {
Id: response.body.items[i].id,
IpuCode: response.body.items[i].ipuCode,
Description: response.body.items[i].description,
PackSize: response.body.items[i].packSize,
PackType: response.body.items[i].packType,
};
cy.log(Item); // 在这里可以正常访问 Item
});
// 尝试在这里重用 Item
// cy.addItemToShoppingCart(Item.IpuCode, pharmacyId, Item.Id, currentDateTime);
// 结果:ReferenceError: Item is not definedItem 变量是在 cy.then() 的回调函数作用域内定义的。当 Cypress 执行到 cy.addItemToShoppingCart 这行代码时,外部作用域并不知道 Item 变量的存在,因为 cy.then() 的回调函数可能尚未执行,或者即使执行了,其内部变量也不会自动暴露到外部命令队列中。Cypress 的命令是排队执行的,而不是像普通 JavaScript 那样立即执行。
Cypress 提供了一种强大且优雅的机制来解决这个问题,即使用别名(Aliases)。别名允许我们将数据、DOM 元素或路由请求保存起来,并在测试的任何后续步骤中通过其别名进行引用。对于复杂的数据对象,我们可以使用 cy.wrap() 命令将其包装成一个可别名的 Cypress Subject。
首先,在获取到服务器响应并构建出所需的对象后,使用 cy.wrap() 将该对象包装起来,并通过 .as() 命令为其指定一个别名。
cy.wait('@pageLoaded').then(({ response }) => {
expect(response.statusCode).to.equal(200);
let i = randomItem();
// 从响应中提取数据并构建对象
const itemData = {
id: response.body.items[i].id,
ipuCode: response.body.items[i].ipuCode,
description: response.body.items[i].description,
packSize: response.body.items[i].packSize,
packType: response.body.items[i].packType,
};
// 使用 cy.wrap() 包装对象并创建别名
cy.wrap(itemData).as('itemDetails'); // 建议使用驼峰命名法
cy.log('Aliased Item Details:', itemData);
});在上述代码中,我们将从响应中解析出的商品信息封装成 itemData 对象,然后使用 cy.wrap(itemData).as('itemDetails') 将其保存为名为 'itemDetails' 的别名。
要访问已保存的别名对象,需要使用 cy.get('@aliasName') 命令。这个命令会从 Cypress 的内部存储中检索别名所指向的值,并将其作为下一个命令的主题(subject)传递。通常,我们会紧接着使用 .then() 回调函数来处理这个别名对象。
// 在测试的后续步骤中
cy.get('@itemDetails').then(item => {
// 此时,item 变量就是我们之前保存的 itemDetails 对象
cy.log('Reusing Aliased Item:', item);
// 现在可以安全地访问 item 对象的属性
cy.addItemToShoppingCart(item.ipuCode, pharmacyId, item.id, currentDateTime);
// ... 其他使用 item 属性的 Cypress 命令
});通过这种方式,cy.get('@itemDetails') 将等待 itemDetails 别名可用(即 cy.wrap().as('itemDetails') 命令执行完毕),然后将其值传递给 .then() 回调函数。这样就确保了数据的正确传递和可用性,避免了作用域问题。
// Cypress 测试文件
describe('商品购物车功能测试', () => {
let pharmacyId = 'somePharmacyId'; // 示例变量
let currentDateTime = new Date().toISOString(); // 示例变量
function randomItem() {
// 模拟随机选择一个商品索引
return Math.floor(Math.random() * 5); // 假设有5个商品
}
it('应该能够从响应中获取商品并添加到购物车', () => {
// 1. 访问页面并等待 API 响应
cy.visit('/some-page'); // 假设页面会触发一个名为 '@pageLoaded' 的 API 请求
cy.intercept('GET', '/api/items*', { fixture: 'items.json' }).as('pageLoaded'); // 模拟 API 响应
cy.wait('@pageLoaded').then(({ response }) => {
expect(response.statusCode).to.equal(200);
let i = randomItem();
// 2. 从响应中解析数据并创建对象
const itemData = {
id: response.body.items[i].id,
ipuCode: response.body.items[i].ipuCode,
description: response.body.items[i].description,
packSize: response.body.items[i].packSize,
packType: response.body.items[i].packType,
};
// 3. 将对象保存为 Cypress 别名
cy.wrap(itemData).as('itemDetails');
cy.log('已将商品详情保存为别名:', itemData);
});
// 4. 在后续测试步骤中重用别名对象
cy.get('@itemDetails').then(item => {
cy.log('从别名中获取商品:', item);
// 假设 cy.addItemToShoppingCart 是一个自定义 Cypress 命令
cy.addItemToShoppingCart(item.ipuCode, pharmacyId, item.id, currentDateTime);
// 进一步的断言或操作
cy.get('.shopping-cart-item').should('contain', item.description);
cy.get('.shopping-cart-item').should('contain', item.packSize);
});
});
});
// 假设在 cypress/support/commands.js 中定义了自定义命令
// Cypress.Commands.add('addItemToShoppingCart', (ipuCode, pharmacyId, itemId, dateTime) => {
// cy.request('POST', '/api/add-to-cart', {
// ipuCode,
// pharmacyId,
// itemId,
// dateTime
// }).then(response => {
// expect(response.status).to.equal(200);
// cy.log('商品已添加到购物车');
// });
// });通过 Cypress 的别名机制,我们可以有效地管理测试中的异步数据流,避免因 JavaScript 作用域和 Cypress 命令队列特性引起的问题。将从响应中获取的复杂数据对象通过 cy.wrap().as() 保存为别名,并在后续步骤中通过 cy.get('@aliasName').then() 安全地重用,是编写健壮、可维护的 Cypress 测试的关键实践之一。这种方法不仅解决了数据访问问题,也提升了测试代码的结构性和可读性。
以上就是在 Cypress 测试中创建和重用对象数据的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号