重塑你的"测试习惯"------避开Cypress的那些"坑"
如果你用过Selenium这类传统测试工具,刚上手Cypress可能会觉得"不习惯":明明写对了代码,却拿不到元素值;想用async/await
处理异步,结果报错......其实不是你写错了,而是Cypress的设计思路和传统工具不一样。这一章咱们就聊聊这些"特殊习惯",帮你少踩坑。
一、Cypress的"坑":这些操作千万别做
1. 别用"同步思维"写异步代码
Cypress的命令都是异步执行 的,和JavaScript的setTimeout
类似,不会立刻执行,而是先"排队",再按顺序执行。比如下面这段代码,你以为会先打印id
,实际却会打印undefined
:
javascript
// 错误示例:以为能拿到元素id
let userId
cy.get('[data-cy=user-id]').then(($el) => {
userId = $el.text() // 这里的赋值是异步的
})
console.log(userId) // 同步代码,会先执行,所以是undefined
正确做法 :用then
回调处理异步结果,所有依赖这个结果的操作都要放在then
里:
javascript
cy.get('[data-cy=user-id]').then(($el) => {
const userId = $el.text()
console.log(userId) // 正确:在回调里处理
// 后续操作也放这里
})
2. 别用箭头函数定义用例或钩子
如果用箭头函数(() => {}
)定义it
或beforeEach
,里面的this
会指向全局,无法使用this.skip()
等方法:
javascript
// 错误示例:箭头函数里的this有问题
it('测试用例', () => {
if (someCondition) {
this.skip() // 报错:this.skip is not a function
}
})
正确做法 :用function
定义,this
才会指向当前测试上下文:
javascript
it('测试用例', function() {
if (someCondition) {
this.skip() // 正常工作:跳过用例
}
})
3. 别用async/await
处理Cypress命令
Cypress命令虽然是异步的,但它不返回Promise
,所以async/await
无效:
javascript
// 错误示例:await对Cypress命令无效
async function test() {
await cy.visit('/login') // 不会等待访问完成
cy.get('input').type('username') // 可能在页面加载完之前执行
}
正确做法 :用Cypress的链式命令或then
回调处理顺序:
javascript
// 正确:链式执行,自动等待前一步完成
cy.visit('/login')
.get('input[name=username]').type('jane')
.get('input[name=password]').type('123')
4. 别在一次测试中访问多个域名
由于浏览器的同源策略限制,Cypress不允许在一个测试用例里访问不同域名(比如先访问http://a.com
,再访问http://b.com
),会直接报错。
解决办法 :如果必须跨域,拆分到多个测试用例;或者确保访问的是同一主域名下的子域(比如http://blog.a.com
和http://shop.a.com
)。
二、Cypress的"独特技巧":这样用更顺手
1. 用闭包保存临时变量
想在测试中保存临时数据(比如接口返回的token),可以用闭包或then
回调:
javascript
describe('测试', function() {
let token // 闭包变量
it('获取token', function() {
cy.request('/api/login').then((res) => {
token = res.body.token // 保存到闭包
})
})
it('使用token', function() {
cy.visit('/profile', {
headers: { Authorization: `Bearer ${token}` } // 直接使用
})
})
})
2. 用as
和get
共享数据
通过cy.wrap().as('别名')
给数据起别名,后续用cy.get('@别名')
访问,适合跨步骤共享:
javascript
// 第一步:保存数据到别名
cy.fixture('user.json').as('userData') // 从fixture取数据
// 或从接口取数据
cy.request('/api/user').then((res) => {
cy.wrap(res.body).as('userInfo')
})
// 后续步骤:用@别名访问
it('使用用户数据', function() {
cy.get('@userData').then((user) => {
cy.get('input[name=username]').type(user.name)
})
})
3. 用Cypress.env()
设置全局变量
想在不同测试文件间共享配置(比如测试环境的域名),可以在cypress.json
里定义环境变量,或运行时通过--env
传入:
json
// cypress.json
{
"env": {
"baseUrl": "http://test.com",
"timeout": 10000
}
}
代码里用Cypress.env('baseUrl')
获取,也可以动态修改:
javascript
Cypress.env('timeout', 15000) // 临时改超时时间