Cypress - 验证别名是否存在或将别名中的文本与“if”语句中的字符串进行比较

问题描述 投票:0回答:1

我正在使用 Cypress 创建 API 自动化套件。

我有不需要登录的 API 测试和需要登录的 API 测试。

我编写了一个名为“callAPI”的实用方法,我的所有测试都会使用它 - 它具有大量错误检查功能,并根据调用者传递到方法中的内容自动使用相关标头/授权令牌来增强调用。

我还有一个“logIn”方法,它创建一个存储授权令牌的别名。

在自动化工程师调用 callAPI 执行需要登录的 API 请求的场景中,我希望能够检测到他们尚未登录并抛出一条错误消息来提示。

方法一: 如果别名“accessToken”不存在,则抛出错误。

问题: 看来 cy.state('aliases') 的使用已被弃用。

方法2: 在“before”挂钩中使用默认值“accessToken not set”创建别名...

before(() => {
    cy.wrap('accessToken not set').as('accessToken');
});

然后在callAPI中,检查别名的值是否等于'accessToken not set'并抛出错误。

问题:

if (useAccessToken && cy.get('@accessToken') == 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.');
}

if (useAccessToken && cy.get('@accessToken').invoke('text') == 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.');
}
    

第一个“if”语句显示“此比较似乎是无意的,因为类型“Chainable”和“string”没有重叠。”

第二个“if”语句显示“此比较似乎是无意的,因为类型“Chainable”和“string”没有重叠。”

我可能错误地认为在“if”语句中比较别名内的文本应该是微不足道的,但显然这不是一件事......?

方法三: 这里的目的是在“当时”内进行比较......

cy.get('@accessToken').then(data => {
    cy.log(data.text());
});

问题: “data.text 不是函数”

方法4: 在我的“if”语句中,断言别名的文本等于“accessToken not set”。

if (useAccessToken && cy.get('@accessToken').should('equal', 'accessToken not set')) {
    throw new Error('callAPI() - access token not set. Log in first.');
}

问题: 如果此断言失败,Cypress 会抛出自己的错误,从而破坏了抛出我的自定义错误的对象。

我的代码:

const apiURL = Cypress.env('apiUrl');

export const APIURLs = {
    Login: `${apiURL}access/login`,
    Info: `${apiURL}info`,
    InfoHealth: `${apiURL}info/health`
};

export class Common {
    logInAndSetAccessToken(emailAddress: string, password: string) {
        this.callAPI({
            requestObject: {
                method: 'POST',
                url: APIURLs.Login,
                body: {
                    email: emailAddress,
                    password: password
                }
            }, useAccessToken: false
        }).then(response => {
            cy.wrap(response.body.tokens.accessToken).as('accessToken');
        });
    }

    /**
     * 'headers' property, along with the following headers are automatically added to requestObject:
     * 
     * 'Content-Type': 'application/json; charset=utf-8'
     * 
     * 'Authorization': `Bearer ${accessToken}`
     */
    callAPI({ url = '', requestObject = {}, useAccessToken = true } = {}) {
        if (url.length > 3 && Object.keys(requestObject).length > 0) {
            throw new Error('callAPI() - method call ambigious. Pass url or requestObject, not both.');
        }

        if (url.length < 4 && Object.keys(requestObject).length == 0) {
            throw new Error('callAPI() - method call missing necessary information to make API call. Pass url or requestObject.');
        }

        if (useAccessToken && cy.get('@accessToken') == 'accessToken not set') { // This comparison appears to be unintentional because the types 'Chainable<JQuery<HTMLElement>>' and 'string' have no overlap.
            throw new Error('callAPI() - access token not set. Log in first.');
        }

        if (Object.keys(requestObject).length > 0) {
            if (!requestObject.method || !requestObject.url || !requestObject.body) {
                throw new Error('callAPI() - method, url or body properties are missing in the requestObject.');
            }

            if (!requestObject.headers) {
                Object.assign(requestObject, { headers: {} });
            }

            if (!requestObject.headers['Content-Type']) {
                Object.assign(requestObject.headers, { 'Content-Type': 'application/json; charset=utf-8' });
            }

            if (useAccessToken && !requestObject.headers['Authorization']) {
                Object.assign(requestObject.headers, { 'Authorization': `Bearer ${cy.get('@accessToken')}` });
            }

            return cy.request(requestObject);
        } else {
            if (url.length < 4) {
                throw new Error('callAPI() - invalid url, cannot call API.');
            }

            return cy.request(url);
        }
    }
}

除非我错过了显而易见的事情(不会让我感到惊讶),有什么办法可以解决这个问题吗?或者我应该只依靠 API 通知自动化工程师他们需要登录?

如果重要的话我正在使用 TypeScript。

感谢您提供的任何帮助。

cypress alias string-comparison
1个回答
0
投票
  1. cy.get('@alias')
    语句本身中直接使用
    if
    值是不可能的,因为
    cy.get()
    会产生
    Chainable<Any>
    类型。因此,比较
    cy.get('@alias')
    cy.get('@alias').invoke('text')
    会产生
    Chainable
    元素,而不是字符串。
  2. 别名会在每次测试的基础上重置,因此,如果您确实想在测试之前设置别名,则需要在
    beforeEach()
    而不是
    before()
    中进行操作。
  3. cy.get()
    命令包含引用存在的隐式断言,这意味着如果未找到
    cy.get('@alias')
    ,Cypress 将无法通过测试。
  4. 幸运的是,我们没有使用cy.get()
    来检索别名值 - 
    我们可以使用Mocha的共享上下文通过this.alias
    来引用值。
因此,考虑到所有这些,一个潜在的解决方案可能是:

describe('tests', () => { beforeEach(() => { cy.wrap('accessToken not set').as('accessToken'); }); it('tests something', function () { if (useAccessToken && this.accessToken == 'accessToken not set') { throw new Error('callAPI() - access token not set. Log in first.'); } }); });
不幸的是,使用 

this.*

 需要在具有 Mocha 共享上下文对象的对象中调用该功能,而您的辅助函数似乎没有该上下文。

相反,我们可以使用赛普拉斯环境变量,这将允许同步检查返回一个字符串变量进行评估。

logInAndSetAccessToken(emailAddress: string, password: string) { this.callAPI({ requestObject: { method: 'POST', url: APIURLs.Login, body: { email: emailAddress, password: password } }, useAccessToken: false }).then(response => { Cypress.env('accessToken', response.body.tokens.accessToken) }); } ... if (useAccessToken && Cypress.env('accessToken') == 'accessToken not set') { // This comparison appears to be unintentional because the types 'Chainable<JQuery<HTMLElement>>' and 'string' have no overlap. throw new Error('callAPI() - access token not set. Log in first.'); }
注意:Cypress 环境变量仅在每个规范的基础上重置,因此访问令牌将在同一规范文件中从一个测试到另一个测试。如果你想避免这种情况,你可以简单地在你的 

beforeEach()

 (
Cypress.env('accessToken', 'accessToken not set');
)
中设置环境变量的“默认”值

© www.soinside.com 2019 - 2024. All rights reserved.