Cypress中如何单次获取JWT Token并在beforeEach中复用?
如何在Cypress中复用单次获取的JWT Token以提升测试效率
问题背景
我正在为新体验模块编写自动化测试,目前后端新API尚未完成,但前端模块已可用,希望提前写好测试,待切换新API后直接启用。新API支持JWT Token认证,当前测试可正常运行但效率低下——每次beforeEach()都会发送POST请求获取Token,存入sessionStorage后再加载页面。测试覆盖3个页面,每个context块对应一个页面的验证逻辑,需求是仅调用一次API获取Token,后续每次加载浏览器时复用该Token。
已尝试的方法及问题
- 在
before()中生成JWT Token,beforeEach()中引用,触发ReferenceError: oc_token未定义 - 用
let替代const,问题依旧 - 尝试
response.body.access_token.invoke('oc_token').as('oc_token'),beforeEach()中用this.oc_token调用,触发TypeError: response.body.access.invoke不是函数 - 用
cy.wrap(response.body.access_token).as('oc_token'),调用this.oc_token时触发TypeError: 无法读取undefined的属性'oc_token' - 用
cy.fixture(response.body.access_token).as('oc_token'),调用时触发Error ENAMETOOLING:名称过长
当前测试代码结构
describe('New experience test', () => { beforeEach(() => { cy.interceptFeatureFlags({ 'new-experience-feature-flags': true }) cy.visit(Cypress.config('baseUrl')); cy.window().then(w => { cy.request({ method: 'POST', url: 'yadda', headers: 'stuff', body: { payload } }).then((response) => { expect(response).property('status').to.equal(200) const oc_token = response.body.access_token; w.sessionStorage.setItem( 'jwt', oc_token ) w.sessionStorage.setItem( 'other_jwt', oc_token ) }) }); cy.fixture('new_experience_context.json').then((context) => { button(cy).click(); }); }); context('first page', () => { it('shows condition', () => { thing.should('exist'); }); }); context('second page', () => { beforeEach(() => { // 跳转到第二页的步骤 }); it('shows condition', () => { thing.should('exist'); }); }); context('third page', () => { beforeEach(() => { // 跳转到第二页的步骤 // 跳转到第三页的步骤 }); it('shows condition', () => { thing.should('exist'); }); }); }); // DOM元素定位函数
尝试过的代码结构
describe('New experience test', () => { before(() => { cy.request({ method: 'POST', url: 'yadda', headers: 'stuff', body: { payload } }).then((response) => { expect(response).property('status').to.equal(200) const oc_token = response.body.access_token; }); }); beforeEach(() => { cy.interceptFeatureFlags({ 'new-experience-feature-flags': true }) cy.visit(Cypress.config('baseUrl')); cy.window().then(w => { w.sessionStorage.setItem( 'jwt', oc_token ) w.sessionStorage.setItem( 'other_jwt', oc_token ) }) }); cy.fixture('new_experience_context.json').then((context) => { button(cy).click(); }); });
解决方案
方法1:使用Cypress环境变量存储Token
在before()中获取Token后存入Cypress环境变量,后续beforeEach()直接取用,无需跨钩子传递局部变量:
describe('New experience test', () => { before(() => { cy.request({ method: 'POST', url: 'yadda', headers: 'stuff', body: { payload } }).then((response) => { expect(response.status).to.equal(200); // 将Token存入Cypress环境变量 Cypress.env('oc_token', response.body.access_token); }); }); beforeEach(() => { cy.interceptFeatureFlags({ 'new-experience-feature-flags': true }); cy.visit(Cypress.config('baseUrl')); cy.window().then(w => { const oc_token = Cypress.env('oc_token'); w.sessionStorage.setItem('jwt', oc_token); w.sessionStorage.setItem('other_jwt', oc_token); }); cy.fixture('new_experience_context.json').then(() => { button(cy).click(); }); }); // 各页面测试用例... });
方法2:使用Cypress别名(正确用法)
若想用别名传递Token,需注意:before()中定义别名后,beforeEach()需用普通函数(不能用箭头函数),通过this.oc_token或cy.get('@oc_token')获取:
describe('New experience test', () => { before(() => { cy.request({ method: 'POST', url: 'yadda', headers: 'stuff', body: { payload } }).then((response) => { expect(response.status).to.equal(200); // 用cy.wrap包装Token后存为别名 cy.wrap(response.body.access_token).as('oc_token'); }); }); beforeEach(function() { // 必须用普通函数,箭头函数会导致this指向丢失 cy.interceptFeatureFlags({ 'new-experience-feature-flags': true }); cy.visit(Cypress.config('baseUrl')); // 通过this访问别名 const oc_token = this.oc_token; cy.window().then(w => { w.sessionStorage.setItem('jwt', oc_token); w.sessionStorage.setItem('other_jwt', oc_token); }); cy.fixture('new_experience_context.json').then(() => { button(cy).click(); }); }); // 各页面测试用例... });
关键注意点
- 局部变量无法跨钩子访问:
before()内定义的局部变量oc_token作用域仅限当前钩子,beforeEach()无法直接读取,这是之前报错的核心原因。 - 箭头函数会破坏
this绑定:若用箭头函数定义beforeEach,this.oc_token会取不到值,必须改用普通函数。 cy.fixture用途错误:该方法用于加载本地JSON文件,不能直接存储Token,因此之前用它存Token会触发文件名过长错误。
内容的提问来源于stack exchange,提问作者rgorowsky
相关产品推荐
相关产品推荐

