从 API 端点加载和使用 Cypress 端到端测试的 fixture
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
我非常喜欢 Cypress,它让我真正体会到了端到端测试的乐趣和吸引力。在本文中,我们将探讨在对应用程序进行端到端测试时可能会遇到的一个常见场景:在fixture测试之前从 API 端点获取数据,并使用该数据编写测试用例。
什么是柏木灯具?
如果您之前没有听说过 fixtures,您可以将其视为预定义的数据,在测试中使用它来执行某些操作。
请参考以下固定装置示例。
{
"username": "yoda",
"password": "secureiam",
"id": 123
}
如果我们的应用程序中有一个端点,例如用于存储用户设置页面,我们可能需要构建以下 URL:
http://www.holocrons.com/123/settings
在这个演示 URL 中,第一个部分是id用户的,因此根据我们的虚拟配置,它将是123。
在 Cypress 中,要导航到某个 URL,您需要使用命令visit,因此您可能需要按如下方式构建您的操作:
cy.visit('/123/settings')
然而,问题在于我们将用户 ID 硬编码到了测试中。在大多数情况下,这会成为一个问题,因为除非我们的后端和数据库配置为使用特定的 ID,否则如果该用户由于某种原因不再存在,那么我们所有的端到端测试都会失败。
第一种方法是创建本地测试用例。你需要进入 Cypress 的文件夹结构,并user.json在指定的fixtures文件夹内创建一个文件。然后,将演示测试用例的内容粘贴到该文件中。
# user.json
{
"username": "yoda",
"password": "secureiam",
"id": 123
}
现在,为了在测试中使用此 fixture,我们可以beforeEach在 spec 文件中设置一个钩子,并按如下方式加载它。
describe('my tests', () => {
beforeEach(function() {
cy.fixture('path/to/user.json').as('userData').then(() => {
// Do some things like setting up routes.
});
})
})
Cypressfixture方法接受一个字符串参数,该参数指向fixture文件所在的位置。此路径基于根cypress.json文件所在的位置。
文件加载到 fixture 后,您可以使用then代码块来触发一些额外的操作,例如设置您的cy.server()路由。
请注意,在本例中,我们使用 `<alias>` 设置了一个别名.as('userData')。如果您稍后要在代码块之外的测试中使用已加载的 fixture 数据,这一点非常重要then。
现在让我们使用新加载的 fixtureuserData来实际设置一些路由。因为我们位于then代码块内,所以有两种选择:您可以将新加载的 fixture 作为参数传递给方法fixture,或者您可以将this其作为属性访问。
在这些示例中,我们将使用这种this方法,但请注意——为了this保持正确的上下文,我们必须使用常规function ()语法,而不是箭头函数it!否则,undefined由于箭头函数的this上下文机制,您将会遇到错误。
让我们router为设置页面创建一个别名。
describe('my tests', () => {
beforeEach(function() {
cy.fixture('path/to/user.json').as('userData').then(() => {
// Do some things like setting up routes.
cy.server()
cy.route(`${this.userData.id}/settings`).as('userSettings')
});
})
})
注意,在本例中,我们this.userData.id显式地声明了 `user` 属性route需要指向用户 ID 123,也就是在 fixture 中声明的那个 ID。现在,我们可以通过该this.userData属性访问 user.json 文件中的任何数据。
以下是it代码块内的另一个例子。
it('shows the users username in the header', function() {
cy.visit(`${this.userData.id}/settings`)
cy.contains('h1', this.userData.username)
})
我们的测试现在不再需要硬编码。但是,如果我们的测试是由某种函数或接口驱动,该函数或接口会向数据库中填充虚拟数据,会发生什么情况呢?接下来,我们将探讨同样的问题和解决方案,但这次是通过fixtureAPI 接口加载数据。
加载基于 API 的测试数据
为了从 API 获取我们的 fixture,我们将创建一些 Cypress 自定义命令,以便更轻松地在测试中使用代码。
请打开您的commands.js文件,我们开始吧。
Cypress.Commands.add('loadFixture', (savePath) => {
cy.request({
method: 'post',
url: `api/path/to/fixture/endpoint`,
timeout: 50000
}).then(res => {
cy.log('Fixture loaded from API');
cy.writeFile(savePath, res.body);
cy.log('Fixture written to disk');
});
});
让我们仔细看看这段代码。首先,我们创建一个新的 Cypress 命令并将其命名为 `<command>` loadFixuture,它将接收一个名为 `<parameter>` 的参数savePath。这是一个字符串,表示我们的 fixture 在磁盘上的保存路径。
在我们的命令中,首先调用cy.requestAPI 向 fixture 端点发送网络请求。该端点应返回 JSON 响应,因此请务必根据应用程序的需求调整参数——当然,您也可以根据需要将参数传递到请求体或查询字符串中。更多选项,url请参阅相关文档。cy.request
请求完成后,我们会调用一个then回调函数来获取请求的结果——其中res包含来自 API 的响应。
我们编写几条cy.log语句,以便从 Cypress 日志中跟踪正在发生的事情,最后调用一个cy.writeFile命令,该命令使用savePath我们传递给该命令的参数以及body来自网络响应的数据。这将把 JSON 数据写入文件。
此时你可能想知道为什么要将网络结果写入文件,我们需要将此信息保存在文件中,以便稍后可以使用命令读取它cy.fixture。
有了新的命令,我们现在可以更新测试以使用新的加载方式。
describe('my tests', () => {
before(function() {
cy.loadFixture('path/to/user.json')
})
beforeEach(function() {
cy.fixture('path/to/user.json').as('userData').then(() => {
// Do some things like setting up routes.
});
})
})
请注意,我们现在正在下载挂钩上的设备before,这样可以确保它只下载一次,因为这种类型的请求通常会给服务器和数据库带来很大的负担。
现在,每当我们beforeEach像以前那样调用它时,我们实际上都会针对在测试开始之前已经下载并写入的文件,这也保证了在必要时您将使用新生成的数据。
一如既往,感谢阅读,欢迎在推特上与我分享您的评论:@marinamosti
PS. 牛油果万岁🥑
PSS. ❤️🔥🐶☠️