Cypress 选择器 #
基本选择器 #
cy.get() #
cy.get() 是最常用的选择器方法,支持 CSS 选择器语法:
javascript
// ID 选择器
cy.get('#username');
// 类选择器
cy.get('.btn-primary');
// 标签选择器
cy.get('button');
// 属性选择器
cy.get('[type="submit"]');
// 组合选择器
cy.get('button.btn-primary[type="submit"]');
选择器类型 #
javascript
// 后代选择器
cy.get('form input');
// 子选择器
cy.get('ul > li');
// 相邻兄弟选择器
cy.get('h1 + p');
// 通用兄弟选择器
cy.get('h1 ~ p');
推荐的选择器策略 #
优先级排序 #
text
1. data-cy / data-test / data-testid ⭐⭐⭐⭐⭐ 最推荐
2. 语义化标签 + 可访问性属性 ⭐⭐⭐⭐
3. ID 选择器 ⭐⭐⭐
4. 类选择器(稳定的类名) ⭐⭐
5. CSS 选择器组合 ⭐
6. 文本内容选择 ⚠️ 谨慎使用
data 属性选择器(最佳实践) #
html
<!-- 应用代码 -->
<button data-cy="submit-button">提交</button>
<input data-cy="email-input" type="email" />
<div data-cy="error-message">错误信息</div>
javascript
// 测试代码
cy.get('[data-cy="submit-button"]').click();
cy.get('[data-cy="email-input"]').type('test@example.com');
cy.get('[data-cy="error-message"]').should('be.visible');
为什么使用 data 属性? #
text
┌─────────────────────────────────────────────────────────────┐
│ 选择器稳定性对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ❌ CSS 类名 │
│ .btn-primary → .btn-primary-v2 │
│ 样式重构后测试失败 │
│ │
│ ❌ ID │
│ #submit-btn → #submit-button │
│ ID 变更后测试失败 │
│ │
│ ❌ DOM 结构 │
│ form > div > input → form > fieldset > input │
│ 结构调整后测试失败 │
│ │
│ ✅ data 属性 │
│ [data-cy="submit"] → [data-cy="submit"] │
│ 始终稳定,与样式和结构无关 │
│ │
└─────────────────────────────────────────────────────────────┘
文本选择器 #
cy.contains() #
根据文本内容选择元素:
javascript
// 精确匹配
cy.contains('登录').click();
// 部分匹配
cy.contains('欢迎').should('be.visible');
// 正则表达式
cy.contains(/^用户\d+$/).click();
// 在特定范围内查找
cy.get('.nav').contains('首页').click();
// 忽略大小写
cy.contains('login', { matchCase: false });
contains 选择器 #
javascript
// 使用 :contains 选择器
cy.get('button:contains("提交")');
cy.get('div:contains("错误")');
// 组合使用
cy.get('.message:contains("成功")').should('have.class', 'success');
过滤选择器 #
.filter() #
javascript
// 过滤可见元素
cy.get('.item').filter(':visible');
// 过滤隐藏元素
cy.get('.item').filter(':hidden');
// 过滤特定属性
cy.get('input').filter('[disabled]');
// 过滤函数
cy.get('.product').filter(($el) => {
return $el.data('price') > 100;
});
.find() #
在已选元素的子元素中查找:
javascript
// 在 form 中查找 input
cy.get('form').find('input');
// 在列表中查找特定项
cy.get('.menu').find('.active');
// 链式调用
cy.get('.container')
.find('.section')
.find('.item')
.first();
.children() #
选择直接子元素:
javascript
// 所有子元素
cy.get('ul').children();
// 特定子元素
cy.get('ul').children('.active');
// 第 N 个子元素
cy.get('ul').children().eq(2);
位置选择器 #
.first() / .last() #
javascript
// 第一个元素
cy.get('.item').first();
// 最后一个元素
cy.get('.item').last();
.eq() #
javascript
// 第 3 个元素(从 0 开始)
cy.get('.item').eq(2);
// 倒数第 2 个元素
cy.get('.item').eq(-2);
.next() / .prev() #
javascript
// 下一个兄弟元素
cy.get('.active').next();
// 上一个兄弟元素
cy.get('.active').prev();
// 后面所有兄弟元素
cy.get('.active').nextAll();
// 前面所有兄弟元素
cy.get('.active').prevAll();
// 所有兄弟元素
cy.get('.active').siblings();
.parent() / .parents() #
javascript
// 直接父元素
cy.get('input').parent();
// 所有祖先元素
cy.get('input').parents();
// 特定祖先元素
cy.get('input').parents('form');
// 直到某个祖先(不包含)
cy.get('input').parentsUntil('.container');
.closest() #
javascript
// 最近的匹配祖先
cy.get('input').closest('form');
cy.get('.item').closest('.container');
状态选择器 #
可见性 #
javascript
// 可见元素
cy.get('.item:visible');
// 隐藏元素
cy.get('.item:hidden');
// 判断可见性
cy.get('.modal').should('be.visible');
cy.get('.hidden').should('not.be.visible');
表单状态 #
javascript
// 禁用状态
cy.get('button:disabled');
cy.get('input:enabled');
// 选中状态
cy.get('input:checked');
cy.get('input:unchecked');
// 聚焦状态
cy.get('input:focus');
cy.get('input:focused');
内容状态 #
javascript
// 空元素
cy.get('input:empty');
// 包含文本
cy.get('div:contains("文本")');
// 有子元素
cy.get('div:has(p)');
属性选择器 #
基本属性 #
javascript
// 有某个属性
cy.get('[disabled]');
// 属性等于值
cy.get('[type="email"]');
// 属性不等于值
cy.get('[type!="email"]');
// 属性包含值
cy.get('[class*="btn"]');
// 属性以值开头
cy.get('[class^="btn-"]');
// 属性以值结尾
cy.get('[class$="-primary"]');
多属性组合 #
javascript
// 同时满足多个属性
cy.get('[type="submit"][disabled]');
// data 属性组合
cy.get('[data-cy="form"][data-valid="true"]');
表单选择器 #
输入类型 #
javascript
// 文本输入
cy.get('input[type="text"]');
cy.get(':text');
// 密码输入
cy.get('input[type="password"]');
cy.get(':password');
// 邮箱输入
cy.get('input[type="email"]');
cy.get(':email');
// 数字输入
cy.get('input[type="number"]');
cy.get(':number');
// 复选框
cy.get('input[type="checkbox"]');
cy.get(':checkbox');
// 单选框
cy.get('input[type="radio"]');
cy.get(':radio');
// 文件上传
cy.get('input[type="file"]');
cy.get(':file');
表单状态 #
javascript
// 必填字段
cy.get('input:required');
// 只读字段
cy.get('input:read-only');
// 有效字段
cy.get('input:valid');
// 无效字段
cy.get('input:invalid');
高级选择器 #
.within() #
在特定范围内执行命令:
javascript
cy.get('.login-form').within(() => {
// 这里的选择器只在 .login-form 范围内查找
cy.get('input[name="username"]').type('user');
cy.get('input[name="password"]').type('pass');
cy.get('button').click();
});
.root() #
选择根元素:
javascript
// 获取 document 根元素
cy.root();
// 在 within 中获取当前上下文
cy.get('.form').within(() => {
cy.root().should('have.class', 'form');
});
.shadow() #
访问 Shadow DOM:
javascript
// 进入 Shadow DOM
cy.get('my-component').shadow().find('.inner-element');
// 链式操作
cy.get('custom-card')
.shadow()
.find('.card-title')
.should('contain', 'Hello');
选择器最佳实践 #
1. 使用 data 属性 #
html
<!-- ✅ 好的做法 -->
<button data-cy="add-to-cart" data-product-id="123">
加入购物车
</button>
<!-- ❌ 不好的做法 -->
<button class="btn btn-primary btn-lg mt-3">
加入购物车
</button>
javascript
// ✅ 好的做法
cy.get('[data-cy="add-to-cart"]').click();
// ❌ 不好的做法
cy.get('.btn.btn-primary.btn-lg').click();
2. 避免过于具体的选择器 #
javascript
// ❌ 太具体,容易因结构变化而失败
cy.get('body > div.container > div.row > div.col-md-6 > form > button');
// ✅ 使用 data 属性,简洁稳定
cy.get('[data-cy="submit-button"]');
3. 使用语义化选择器 #
javascript
// ✅ 结合语义标签
cy.get('nav').find('[data-cy="menu-item"]');
cy.get('article').find('[data-cy="title"]');
cy.get('aside').find('[data-cy="widget"]');
// ✅ 使用 ARIA 属性
cy.get('[role="button"]');
cy.get('[aria-label="关闭"]');
cy.get('[aria-labelledby="dialog-title"]');
4. 创建选择器常量 #
javascript
// cypress/support/selectors.js
export const SELECTORS = {
LOGIN: {
FORM: '[data-cy="login-form"]',
USERNAME: '[data-cy="username-input"]',
PASSWORD: '[data-cy="password-input"]',
SUBMIT: '[data-cy="login-button"]',
ERROR: '[data-cy="error-message"]'
},
CART: {
CONTAINER: '[data-cy="cart-container"]',
ITEM: '[data-cy="cart-item"]',
TOTAL: '[data-cy="cart-total"]',
CHECKOUT: '[data-cy="checkout-button"]'
}
};
// 使用
import { SELECTORS } from '../support/selectors';
cy.get(SELECTORS.LOGIN.USERNAME).type('user');
cy.get(SELECTORS.LOGIN.SUBMIT).click();
5. 动态选择器 #
javascript
// 使用变量构建选择器
const productId = 123;
cy.get(`[data-product-id="${productId}"]`);
// 使用函数生成选择器
const getItemSelector = (id) => `[data-cy="item-${id}"]`;
cy.get(getItemSelector(1)).click();
// 使用模板字符串
const section = 'profile';
cy.get(`[data-section="${section}"]`);
选择器调试 #
打印匹配元素 #
javascript
cy.get('.item').then(($items) => {
console.log(`找到 ${$items.length} 个元素`);
$items.each((index, el) => {
console.log(index, el.textContent);
});
});
验证选择器 #
javascript
// 检查元素数量
cy.get('.item').should('have.length', 5);
// 检查元素存在
cy.get('.item').should('exist');
// 检查元素可见
cy.get('.item').should('be.visible');
使用选择器测试工具 #
javascript
// 在浏览器控制台中测试选择器
// 打开 DevTools,在 Console 中输入:
document.querySelectorAll('[data-cy="submit-button"]');
// 或使用 Cypress 命令
cy.get('[data-cy="submit-button"]').debug();
完整示例 #
javascript
describe('选择器示例', () => {
beforeEach(() => {
cy.visit('/products');
});
it('使用各种选择器策略', () => {
// data 属性选择器(推荐)
cy.get('[data-cy="product-list"]').should('exist');
// 文本选择器
cy.contains('商品列表').should('be.visible');
// 组合选择器
cy.get('.product-card[data-cy="featured"]').first().click();
// 过滤选择器
cy.get('.product-item').filter(':visible').should('have.length.gt', 0);
// 在范围内查找
cy.get('[data-cy="product-list"]').within(() => {
cy.get('[data-cy="product-item"]').first().click();
});
// 相对位置选择
cy.get('[data-cy="product-item"]').first()
.next()
.should('have.attr', 'data-cy', 'product-item');
// 表单选择器
cy.get('[data-cy="search-form"]').within(() => {
cy.get('input[type="text"]').type('手机');
cy.get('button[type="submit"]').click();
});
// 状态选择器
cy.get('button:disabled').should('exist');
cy.get('input:focus').should('have.attr', 'name', 'search');
});
});
下一步 #
现在你已经掌握了 Cypress 选择器的使用方法,接下来学习 断言 了解如何验证测试结果!
最后更新:2026-03-28