【问题标题】:Cypress: Re-use auth token across multiple API tests赛普拉斯:在多个 API 测试中重复使用身份验证令牌
【发布时间】:2019-11-23 14:19:01
【问题描述】:

我有一个生成令牌的 Rest API。此会话令牌在多个 REST API 中用作授权持有者令牌。我以此为参考:https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/logging-in__jwt/cypress/integration/spec.js

但是,在该示例中,生成令牌的函数嵌入在测试中。我试图创建一个应该在本地存储的自定义命令,但它没有被测试拾取。请注意,自定义命令中不包含返回值。

我在 support/commands.js 下的代码:

let identity
Cypress.Commands.add('postToken', () => {
    cy.request({
        method: 'POST',
        url: Cypress.env('api_identity_url'), //get from cypress.env.json
        form: true, //sets to application/x-www-form-urlencoded
        body: {
            grant_type: 'client_credentials',
            scope: 'xero_all-apis'
        },
        auth: {
            username: Cypress.env('api_identity_username'),
            password: Cypress.env('api_identity_password')
        }
    })
        .its('body')
        .then((response) => {
            identity = response
            window.localStorage.setItem('identity', JSON.stringify(identity))
            cy.log(identity.access_token)
        })
})

我的测试

context('Check token details', () => {
  it('Check token', () => {
      cy.postToken()
      const bToken = window.localStorage.getItem('identity')
      cy.log(bToken)
  })
})

当我运行测试时,日志显示“身份”的 null 值。但是,它会在我放置cy.log(identity.access_token) 的自定义命令中显示当前值 我尝试使用 cy.writeFile 但我认为这不是一个干净的方法。必须有某种方式可以在函数和不同类之间传递数据。

示例 JSON 格式:

{
  "token": "<this is the value I would like to use for other API's authorisation bearer token>",
  "expires_in": 1200,
  "token_type": "Bearer"
}

【问题讨论】:

    标签: javascript rest api automation cypress


    【解决方案1】:

    您可以使用 cypress-localstorage-commands 包在测试之间保持 localStorage。

    support/commands.js:

    import "cypress-localstorage-commands";
    
    Cypress.Commands.add('postToken', () => {
      cy.request({
        method: 'POST',
        url: Cypress.env('api_identity_url'), //get from cypress.env.json
        form: true, //sets to application/x-www-form-urlencoded
        body: {
          grant_type: 'client_credentials',
          scope: 'xero_all-apis'
        },
        auth: {
          username: Cypress.env('api_identity_username'),
          password: Cypress.env('api_identity_password')
        }
      })
      .its('body')
      .then(identity => {
        cy.setLocalStorage("identity_token", identity.token);
      });
    });
    

    在您的测试中:

    describe("postToken", ()=> {
      before(() => {
        cy.postToken();
        cy.saveLocalStorage();
      });
    
      beforeEach(() => {
        cy.restoreLocalStorage();
      });
    
      it("should exist identity in localStorage", () => {
        cy.getLocalStorage("identity_token").should("exist");
        cy.getLocalStorage("identity_token").then(token => {
          console.log("Identity token", token);
        });
      });
    
      it("should still exist identity in localStorage", () => {
        cy.getLocalStorage("identity_token").should("exist");
        cy.getLocalStorage("identity_token").then(token => {
          console.log("Identity token", token);
        });
      });
    });
    

    【讨论】:

    • 谢谢。这是一半,这很好。但是,我想使用该 value (身份)在 API 测试之间传递。例如,我想使用identity.token(我的帖子中包含示例 JSON 格式)作为另一个 API 的授权持有者令牌。我从文档中找不到很清楚。
    • 在我的示例中,我使用“should”命令来检查 localStorage “identity”是否真的设置了,但您也可以使用“then”子命令来获取真正的 localStorage 值。我更新了代码示例以向您展示如何。
    • 谢谢哈维尔。我现在明白了。 JS 和 Cypress 的异步语言相当新。仅供参考,我在代码中添加了var obj = JSON.parse(token) 来捕获数据。
    • 在我的示例中 JSON.parse(token) 不是必需的,因为令牌是保存到 localStorage 的唯一属性,而不是完整的对象,但是,如果您使用 JSON.stringify 保存完整的对象,那就对了,您需要在读取对象时再次调用JSON.parse 进行转换。
    • 嗨@javier-brea 我知道本地存储已经解决了我的问题,但是如果我必须捕获在其请求中调用令牌的API 的响应怎么办。我如何在我的测试中调用它?示例:cy.newAPI(token) 那么我想将该 newAPI 的响应 JSON 详细信息保存到另一组 localStorage 函数中吗?我也很高兴单独发布此内容,但我认为最好将其放在这里以便您收到通知。
    【解决方案2】:

    感谢 Javier 向我展示了 cypress-localstorage-commands 包。我开始使用它。在那之前,我曾经像这样获得登录令牌。

    describe('Record audit', () => {
        let token = null;
    
        before(() => {
            cy.login().then((responseToken) => { // or postToken() in your case
                token = responseToken;
            });
        });
    
        it('I can use the token here', () => {
            cy.log(token);
        });
    });
    

    唯一的区别是我的login 命令返回了令牌。在你的代码中应该是这样的

    // commands.js
    
    .then((response) => {
        identity = response
        window.localStorage.setItem('identity', JSON.stringify(identity))
        cy.log(identity.access_token)
        return identity.access_token
    })
    

    【讨论】:

    • 如果我想捕获来自不同 API 的多个值(例如用户 ID、令牌 ID 等),那么 localstorage 仍然是最佳选择吗?从你上面的例子,token = responseToken,我假设 cy.login 总是返回一个“responseToken”值
    【解决方案3】:

    我在 commmand.js 中使用了这段代码

    var headers_login = new Headers()
    headers_login.append('Content-Type', 'application/json')
    
    Cypress.Commands.add('get_token', (username, password)=>{
    var token = ""
    cy.request({
        method: 'POST',
        url: Cypress.env("api") + "users/getToken",
        failOnStatusCode: false,
        json: true,
        form: true,
        body: {username: username, password: password},
        headers: headers_login
     }).then((json) => {
        //cy.setLocalStorage('token', json.body.response.data.token)   
        token = json.body.response.data.token
        return token;
     }) }) 
    

    在您的测试中添加此代码后

    describe('test', ()=>{
     before(()=>{
        cy.get_token('username', 'password').then(youToken => {
            cy.visit('/', {
                 onBeforeLoad: function (window) {
                    window.localStorage.setItem('token', youToken);
                 }
             })
         }) 
         cy.close_welcome()        
     })
     it('test 001', ()=>{
           // contain test 
     })})
    afterEach(()=>{cy.clearLocalStorage('token')})
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      • 1970-01-01
      • 2022-01-03
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      • 2022-11-02
      相关资源
      最近更新 更多