如何将 Cypress 配置为更长时间(或无限期)等待 BaseUrl?
How To Configure Cypress To Wait Longer (or Indefinitely) for BaseUrl?
我在 docker-compose.yml
到 运行 端到端测试中使用这个 Cypress 图像:cypress/included:6.1.0
当测试 运行ner 启动时,它将验证是否可以访问位于 baseUrl
的服务器。如果不是,则重试 3 次。
我的服务和 Web 服务器需要更多时间才能启动。
如何增加此检查的超时 and/or 重试次数。
最好,在我的例子中,我想要一个重试直到成功的策略,即无限期 retries/wait。
我检查了 Timeouts 部分和 cypress.json
文档。但是 none 这些超时或重试似乎与此行为有关。
有这个设置吗?
澄清一下:这不是我作为规范的一部分实施(或想要)的检查。据我所知,这是图像中默认命令 cyprus run
的一个特性。如果可能的话,我想在不添加或修改测试本身的情况下进行配置。
这是 cypress 在容器中启动时的 docker-compose 控制台输出:
cypress_1 | Cypress could not verify that this server is running:
cypress_1 |
cypress_1 | > http://localhost:5000
cypress_1 |
cypress_1 | We are verifying this server because it has been configured as your `baseUrl`.
cypress_1 |
cypress_1 | Cypress automatically waits until your server is accessible before running tests.
cypress_1 |
cypress_1 | We will try connecting to it 3 more times...
cypress_1 | We will try connecting to it 2 more times...
cypress_1 | We will try connecting to it 1 more time...
cypress_1 |
cypress_1 | Cypress failed to verify that your server is running.
cypress_1 |
cypress_1 | Please start this server and then run Cypress again.
cypress_1 exited with code 1
在使用 wait-on or start-server-and-test.
之类的实用程序调用 cypress run
之前,您应该确保您的服务器处于 运行ning 状态
Cypress 对 baseUrl
的检查是最后的礼貌检查,这样您就不会 运行 在不是 运行 的服务器上通过整个测试套件。
有关确保服务器 运行ning 在 运行ning 赛普拉斯之前的提示,请在此处查看赛普拉斯文档:https://on.cypress.io/continuous-integration#Boot-your-server
免责声明
;在大多数情况下,您应该确保服务器在启动 cypress 之前 运行。
但是
我们主要使用 Cypress 进行 API 测试(目前),我们需要支持的工作流程之一包括服务器重启。当然,我们可以在继续下一步之前插入任意长的 cy.wait(30000);
,但这并不优雅,而且会浪费很多时间,尤其是如果你像我一样最终 运行一遍又一遍的测试。
由于 Cypress 并不真正以我们通常习惯的异步方式工作,我们提出的解决方案是使用 task.
将此添加到 plugins/index.js
:
const https = require("https");
const { URL } = require("url");
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
require('@cypress/code-coverage/task')(on, config)
on("task", {
async waitForServerResponse({ server_url }) {
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function makeRequest({ hostname, port, path }) {
return new Promise((resolve, reject) => {
const options = {
hostname,
port,
path,
body: {},
method: 'POST'
}
const req = https.request(options, response => {
response.on('data', d => {
resolve(d.toString());
});
});
req.on('error', error => {
reject(error);
});
req.end();
});
}
async function recursiveGet(retry = 1) {
try {
const res = await makeRequest({ hostname, port, path });
if (res?.code?.includes("ECONNREFUSED") || res?.code?.includes("ECONNRESET")) {
await sleep(1000);
await recursiveGet(retry + 1);
}
}
catch(error) {
if (error?.code?.includes("ECONNREFUSED") || error?.code?.includes("ECONNRESET")) {
await sleep(1000);
await recursiveGet(retry + 1);
}
}
}
if (!server_url) {
server_url = config.baseUrl;
}
const parsedUrl = new URL(server_url);
const hostname = parsedUrl?.hostname ?? "localhost";
const port = parsedUrl?.port ?? 443;
const path = parsedUrl?.pathname ?? "/";
return new Promise(async (resolve, reject) => {
// tasks should not resolve with undefined
setTimeout(() => reject(new Error("Timeout")), 60000);
await recursiveGet();
resolve(true);
});
}
});
return config;
};
并在你的测试中调用它:
it("Restarts the server", () => {
// Restart the server
cy.systemRestart().then(({ body, status }) => { // server returns success before actually restarting
expect(status).to.equal(200);
expect(body).property("success").to.eq(true);
cy.wait(1000); // initial wait
cy.task("waitForServerResponse", { server_url: server_url + "/auth/token" });
cy.login();
cy.adminIndex().then(({ body, status }) => {
if (body?.properties) {
expect(status).to.equal(200);
expect(body).property("properties").to.be.a("object");
const bootedAt = new Date(body.properties.system.bootedAt).getTime();
const now = new Date().getTime();
const diff = Math.ceil(Math.abs(now - bootedAt) / 1000); // ms -> s
expect(diff).to.be.lessThan(20); // seconds
}
});
});
});
这将轮询服务器(任何给定端点,我选择了 /auth/token
),如果连接被拒绝或重置,它将等待 1 秒并重试。该任务只会在收到服务器响应后 return。
我在 docker-compose.yml
到 运行 端到端测试中使用这个 Cypress 图像:cypress/included:6.1.0
当测试 运行ner 启动时,它将验证是否可以访问位于 baseUrl
的服务器。如果不是,则重试 3 次。
我的服务和 Web 服务器需要更多时间才能启动。
如何增加此检查的超时 and/or 重试次数。
最好,在我的例子中,我想要一个重试直到成功的策略,即无限期 retries/wait。
我检查了 Timeouts 部分和 cypress.json
文档。但是 none 这些超时或重试似乎与此行为有关。
有这个设置吗?
澄清一下:这不是我作为规范的一部分实施(或想要)的检查。据我所知,这是图像中默认命令 cyprus run
的一个特性。如果可能的话,我想在不添加或修改测试本身的情况下进行配置。
这是 cypress 在容器中启动时的 docker-compose 控制台输出:
cypress_1 | Cypress could not verify that this server is running:
cypress_1 |
cypress_1 | > http://localhost:5000
cypress_1 |
cypress_1 | We are verifying this server because it has been configured as your `baseUrl`.
cypress_1 |
cypress_1 | Cypress automatically waits until your server is accessible before running tests.
cypress_1 |
cypress_1 | We will try connecting to it 3 more times...
cypress_1 | We will try connecting to it 2 more times...
cypress_1 | We will try connecting to it 1 more time...
cypress_1 |
cypress_1 | Cypress failed to verify that your server is running.
cypress_1 |
cypress_1 | Please start this server and then run Cypress again.
cypress_1 exited with code 1
在使用 wait-on or start-server-and-test.
之类的实用程序调用cypress run
之前,您应该确保您的服务器处于 运行ning 状态
Cypress 对 baseUrl
的检查是最后的礼貌检查,这样您就不会 运行 在不是 运行 的服务器上通过整个测试套件。
有关确保服务器 运行ning 在 运行ning 赛普拉斯之前的提示,请在此处查看赛普拉斯文档:https://on.cypress.io/continuous-integration#Boot-your-server
免责声明
但是
我们主要使用 Cypress 进行 API 测试(目前),我们需要支持的工作流程之一包括服务器重启。当然,我们可以在继续下一步之前插入任意长的 cy.wait(30000);
,但这并不优雅,而且会浪费很多时间,尤其是如果你像我一样最终 运行一遍又一遍的测试。
由于 Cypress 并不真正以我们通常习惯的异步方式工作,我们提出的解决方案是使用 task.
将此添加到 plugins/index.js
:
const https = require("https");
const { URL } = require("url");
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
require('@cypress/code-coverage/task')(on, config)
on("task", {
async waitForServerResponse({ server_url }) {
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function makeRequest({ hostname, port, path }) {
return new Promise((resolve, reject) => {
const options = {
hostname,
port,
path,
body: {},
method: 'POST'
}
const req = https.request(options, response => {
response.on('data', d => {
resolve(d.toString());
});
});
req.on('error', error => {
reject(error);
});
req.end();
});
}
async function recursiveGet(retry = 1) {
try {
const res = await makeRequest({ hostname, port, path });
if (res?.code?.includes("ECONNREFUSED") || res?.code?.includes("ECONNRESET")) {
await sleep(1000);
await recursiveGet(retry + 1);
}
}
catch(error) {
if (error?.code?.includes("ECONNREFUSED") || error?.code?.includes("ECONNRESET")) {
await sleep(1000);
await recursiveGet(retry + 1);
}
}
}
if (!server_url) {
server_url = config.baseUrl;
}
const parsedUrl = new URL(server_url);
const hostname = parsedUrl?.hostname ?? "localhost";
const port = parsedUrl?.port ?? 443;
const path = parsedUrl?.pathname ?? "/";
return new Promise(async (resolve, reject) => {
// tasks should not resolve with undefined
setTimeout(() => reject(new Error("Timeout")), 60000);
await recursiveGet();
resolve(true);
});
}
});
return config;
};
并在你的测试中调用它:
it("Restarts the server", () => {
// Restart the server
cy.systemRestart().then(({ body, status }) => { // server returns success before actually restarting
expect(status).to.equal(200);
expect(body).property("success").to.eq(true);
cy.wait(1000); // initial wait
cy.task("waitForServerResponse", { server_url: server_url + "/auth/token" });
cy.login();
cy.adminIndex().then(({ body, status }) => {
if (body?.properties) {
expect(status).to.equal(200);
expect(body).property("properties").to.be.a("object");
const bootedAt = new Date(body.properties.system.bootedAt).getTime();
const now = new Date().getTime();
const diff = Math.ceil(Math.abs(now - bootedAt) / 1000); // ms -> s
expect(diff).to.be.lessThan(20); // seconds
}
});
});
});
这将轮询服务器(任何给定端点,我选择了 /auth/token
),如果连接被拒绝或重置,它将等待 1 秒并重试。该任务只会在收到服务器响应后 return。