我正在和Cypress一起创建一个API自动化套件.

我有不需要登录的API测试,也有需要登录的API测试.

我已经编写了一个名为‘allAPI’的实用程序方法,我的所有测试都将使用它--它有一系列错误判断,并根据调用者传递给该方法的内容,自动使用相关的头/授权令牌来增加调用.

我还有一个"登录"方法,它可以创建一个存储授权令牌的别名.

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

方法1: 如果别名‘accesToken’不存在,则抛出错误.

问题: 似乎不赞成使用cy.state('aliases').

方法2: 使用缺省值"accesToken Not Set"在"BEFORE"挂接中创建别名...

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

然后在CallAPI中,判断别名的值是否等于‘accesToken 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’语句中比较别名中的文本应该是微不足道的,但显然这不是一件事...?

方法3: 这里的用意是在"当时"内进行比较……

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

问题: "data.text不是函数"

方法4: 在我的‘if’语句中,断言别名的文本等于‘accesToken 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,如果这很重要.

感谢您所能提供的任何帮助.

推荐答案

谢谢大家的帮助.

最后,我将登录方法和调用API方法分开,如果需要使用访问令牌,则在调用调用API中的登录方法.

export class Common {
    /**
     * Strictly for testing logging in - DOES NOT save access token for future calls.
     */
    logIn(emailAddress: string, password: string) {
        return cy.request({
            method: 'POST',
            url: APIURLs.AccessLogin,
            body: {
                email: emailAddress,
                password: password
            }
        });
    }

    /**
     * For API calls that require logging in/access token, pass userObject.
     */
    callAPI({ url = '', requestObject = {}, useAccessToken = true, userObject = {} } = {}) {
        if (url.length > 0 && Object.keys(requestObject).length > 0) {
            throw new Error('callAPI() - method call ambigious. Pass url or requestObject, not both.');
        }

        if (url.length < (apiURL.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 && Object.keys(userObject).length == 0) {
            throw new Error('callAPI() - cannot use access token without passing a userObject from configuration.');
        }

        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' });
            }
        } else {
            if (url.length < (apiURL.length + 4)) {
                throw new Error('callAPI() - invalid url, cannot call API.');
            }
        }

        if (useAccessToken) {
            return this.logIn(userObject.Email, userObject.m_UIPassword).then(response => {
                const accessToken = response.body.tokens.accessToken;

                if (!requestObject.headers['Authorization']) {
                    Object.assign(requestObject.headers, { 'Authorization': `Bearer ${accessToken}` });
                } else {
                    requestObject.headers['Authorization'] = `Bearer ${accessToken}`;
                }

                return cy.request(requestObject);
            });
        } else {
            if (Object.keys(requestObject).length > 0) {
                return cy.request(requestObject);
            } else {
                return cy.request(url);
            }
        }
    }
}

Typescript相关问答推荐

返回对象的TypScript构造函数隐式地替换This的值来替换super(.)的任何调用者?

Angular 17 -如何在for循环内创建一些加载?

Angular中的其他服务仅获取默认值

使某些类型的字段变为可选字段'

我们过go 不能从TypeScript中的联合中同时访问所有属性?

在MessageStore对象中实现条件类型时出现TypeScrip错误

ANGLE找不到辅助‘路由出口’的路由路径

Material UI / MUI系统:我如何告诉TypeScript主题是由提供程序传递的?

有条件地删除区分的联合类型中的属性的可选属性

推断从其他类型派生的类型

`fetcher.load`是否等同于Get Submit?

笛卡尔产品类型

如何合并两个泛型对象并获得完整的类型安全结果?

转换器不需要的类型交集

跟踪深度路径时按条件提取嵌套类型

将超类型断言为类型脚本中的泛型参数

如何解决&Quot;类型不可分配给TypeScrip IF语句中的类型&Quot;?

可以';t使用t正确地索引对象;联合;索引类型

两个子组件共享控制权

req.files = 未定义(Multer、Express、Typescript)