CAPTCHA chặn các bài kiểm tra E2E tự động. Việc vô hiệu hóa chúng trong quá trình chạy sẽ gây ra hiện tượng lệch môi trường — các lỗi chỉ xuất hiện trong quá trình sản xuất khi CAPTCHA đang hoạt động. CaptchaAI cho phép các bài kiểm tra Cypress của bạn tương tác với CAPTCHA thực, giữ cho môi trường kiểm tra giống hệt với môi trường sản xuất.
Tại sao không tắt CAPTCHA trong các bài kiểm tra?
| Cách tiếp cận | Rủi ro |
|---|---|
| Vô hiệu hóa CAPTCHA trong giai đoạn | Thiếu lỗi tích hợp, sự khác biệt về dòng chảy biểu mẫu |
| Sử dụng các phím kiểm tra (luôn vượt qua) | Không kiểm tra việc chèn mã thông báo, xử lý gọi lại |
| Giải bằng CaptchaAI | Thử nghiệm tương đương sản xuất đầy đủ |
thiết lập
npm install cypress --save-dev
Cấu hình cây bách
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
baseUrl: "https://your-app.com",
defaultCommandTimeout: 120000,
responseTimeout: 120000,
setupNodeEvents(on, config) {
on("task", {
solveCaptcha({ siteUrl, sitekey, type }) {
return solveCaptchaTask(siteUrl, sitekey, type);
},
});
return config;
},
},
env: {
CAPTCHAAI_KEY: "YOUR_API_KEY",
},
});
Trình xử lý tác vụ CaptchaAI
// cypress/plugins/captcha-solver.js
const https = require("https");
function httpPost(url, data) {
return new Promise((resolve, reject) => {
const params = new URLSearchParams(data).toString();
const options = {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
};
const req = https.request(url, options, (res) => {
let body = "";
res.on("data", (c) => (body += c));
res.on("end", () => resolve(JSON.parse(body)));
});
req.on("error", reject);
req.write(params);
req.end();
});
}
function httpGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let body = "";
res.on("data", (c) => (body += c));
res.on("end", () => resolve(JSON.parse(body)));
}).on("error", reject);
});
}
async function solveCaptchaTask(siteUrl, sitekey, type = "recaptcha_v2") {
const API = "https://ocr.captchaai.com";
const key = process.env.CAPTCHAAI_KEY || "YOUR_API_KEY";
const submitData = {
key,
pageurl: siteUrl,
json: "1",
};
if (type === "turnstile") {
submitData.method = "turnstile";
submitData.sitekey = sitekey;
} else {
submitData.method = "userrecaptcha";
submitData.googlekey = sitekey;
}
const submitResp = await httpPost(`${API}/in.php`, submitData);
if (submitResp.status !== 1) {
throw new Error(`Submit failed: ${submitResp.request}`);
}
const taskId = submitResp.request;
// Poll for result
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const params = new URLSearchParams({
key,
action: "get",
id: taskId,
json: "1",
});
const result = await httpGet(`${API}/res.php?${params}`);
if (result.request === "CAPCHA_NOT_READY") continue;
if (result.status !== 1) throw new Error(`Solve failed: ${result.request}`);
return result.request; // The CAPTCHA token
}
throw new Error("CAPTCHA solve timeout");
}
module.exports = { solveCaptchaTask };
Nối dây vào cypress.config.js
// cypress.config.js
const { solveCaptchaTask } = require("./cypress/plugins/captcha-solver");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on("task", {
solveCaptcha({ siteUrl, sitekey, type }) {
return solveCaptchaTask(siteUrl, sitekey, type);
},
});
},
},
});
Lệnh tùy chỉnh
// cypress/support/commands.js
Cypress.Commands.add("solveCaptcha", (options = {}) => {
cy.get("[data-sitekey]", { timeout: 10000 }).then(($el) => {
const sitekey = options.sitekey || $el.attr("data-sitekey");
const siteUrl = options.siteUrl || cy.url();
cy.url().then((url) => {
cy.task("solveCaptcha", {
siteUrl: url,
sitekey,
type: options.type || "recaptcha_v2",
}).then((token) => {
// Inject token
cy.window().then((win) => {
const responseEl = win.document.querySelector(
"#g-recaptcha-response"
);
if (responseEl) {
responseEl.value = token;
}
// Set all hidden response fields
win.document
.querySelectorAll('[name="g-recaptcha-response"]')
.forEach((el) => {
el.value = token;
});
// Trigger callback if exists
if (win.___grecaptcha_cfg) {
const clients = win.___grecaptcha_cfg.clients;
for (const key in clients) {
const client = clients[key];
if (client && typeof client.callback === "function") {
client.callback(token);
}
}
}
});
});
});
});
});
Cypress.Commands.add("solveTurnstile", (options = {}) => {
cy.get("[data-sitekey]", { timeout: 10000 }).then(($el) => {
const sitekey = options.sitekey || $el.attr("data-sitekey");
cy.url().then((url) => {
cy.task("solveCaptcha", {
siteUrl: url,
sitekey,
type: "turnstile",
}).then((token) => {
cy.window().then((win) => {
const input = win.document.querySelector(
'input[name="cf-turnstile-response"]'
);
if (input) input.value = token;
});
});
});
});
});
Ví dụ về thử nghiệm E2E
Luồng đăng nhập bằng reCAPTCHA
// cypress/e2e/login.cy.js
describe("Login with reCAPTCHA", () => {
it("should log in through a CAPTCHA-protected form", () => {
cy.visit("/login");
cy.get("#username").type("testuser");
cy.get("#password").type("securepassword123");
// Solve the CAPTCHA
cy.solveCaptcha();
// Submit
cy.get('button[type="submit"]').click();
// Verify login success
cy.url().should("include", "/dashboard");
cy.get(".welcome-message").should("contain", "Welcome, testuser");
});
});
Luồng đăng ký
// cypress/e2e/register.cy.js
describe("Registration with CAPTCHA", () => {
it("completes registration with all fields + CAPTCHA", () => {
cy.visit("/register");
cy.get("#first-name").type("Test");
cy.get("#last-name").type("User");
cy.get("#email").type("test@example.com");
cy.get("#password").type("StrongPass!123");
cy.get("#confirm-password").type("StrongPass!123");
cy.solveCaptcha();
cy.get("#register-btn").click();
cy.url().should("include", "/verify-email");
});
});
Thanh toán được bảo vệ bằng cửa quay
describe("Checkout with Turnstile", () => {
it("processes payment through Turnstile-protected checkout", () => {
cy.visit("/cart");
cy.get(".checkout-btn").click();
cy.get("#card-number").type("4242424242424242");
cy.get("#expiry").type("12/26");
cy.get("#cvc").type("123");
cy.solveTurnstile();
cy.get("#pay-now").click();
cy.get(".confirmation").should("contain", "Order confirmed");
});
});
Thử lại và xử lý lỗi
// cypress/support/commands.js
Cypress.Commands.add("solveCaptchaWithRetry", (options = {}) => {
const maxRetries = options.retries || 3;
function attempt(retryCount) {
return cy.task("solveCaptcha", {
siteUrl: options.siteUrl,
sitekey: options.sitekey,
type: options.type || "recaptcha_v2",
}).then((token) => {
if (!token && retryCount < maxRetries) {
cy.log(`CAPTCHA retry ${retryCount + 1}/${maxRetries}`);
cy.wait(2000);
return attempt(retryCount + 1);
}
return token;
});
}
return attempt(0);
});
Tích hợp CI/CD
Hành động GitHub
name: E2E Tests
on: [push, pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Run Cypress tests
uses: cypress-io/github-action@v6
env:
CAPTCHAAI_KEY: ${{ secrets.CAPTCHAAI_KEY }}
with:
wait-on: "http://localhost:3000"
start: npm start
Kiểm tra tích hợp Jest
// For teams that also use Jest for API-level CAPTCHA tests
const { solveCaptchaTask } = require("../cypress/plugins/captcha-solver");
test("CaptchaAI solves reCAPTCHA v2", async () => {
const token = await solveCaptchaTask(
"https://www.google.com/recaptcha/api2/demo",
"6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"recaptcha_v2"
);
expect(token).toBeDefined();
expect(token.length).toBeGreaterThan(50);
}, 120000);
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
cy.task timed out |
Quá trình giải CAPTCHA mất quá nhiều thời gian | Tăng taskTimeout trong cấu hình |
| Mã thông báo bị từ chối | Hết hạn trước khi tiêm | Giảm độ trễ giữa giải quyết và gửi |
Không tìm thấy data-sitekey |
CAPTCHA tải động | Thêm cy.wait() rõ ràng hoặc chặn |
| Cuộc gọi lại không được kích hoạt | Tên gọi lại tùy chỉnh | Kiểm tra ___grecaptcha_cfg trong DevTools |
| CI không thành công, vượt qua cục bộ | Thiếu biến env | Thêm CAPTCHAAI_KEY vào bí mật CI |
Câu hỏi thường gặp
Điều này có làm chậm bộ thử nghiệm của tôi không?
Mỗi lần giải CAPTCHA sẽ cộng thêm 15-30 giây. Chạy thử nghiệm CAPTCHA trong một bộ riêng biệt hoặc song song với Cypress Cloud.
Tôi có thể sử dụng tính năng này với thử nghiệm thành phần Cypress không?
Không - kiểm tra thành phần không tải các trang thực. Chỉ sử dụng tùy chọn này cho các thử nghiệm E2E đạt được URL toàn trang có CAPTCHA thực.
Tôi nên kiểm tra bằng CAPTCHA thật hay thử nghiệm chúng?
Kiểm tra bằng CAPTCHA thực trong giai đoạn E2E. Sử dụng mô phỏng trong các bài kiểm tra đơn vị. Điều này đảm bảo sự cân bằng sản xuất đầy đủ.
CaptchaAI có hoạt động song song với Cypress Cloud không?
Vâng. Mỗi máy song song gọi cùng một khóa API. CaptchaAI xử lý các yêu cầu đồng thời.
Hướng dẫn liên quan
- Puppeteer tàng hình + CaptchaAI
- API giải CAPTCHA của Node.js
Bước tiếp theo
- CaptchaAI Quickstart: Lần Giải CAPTCHA Đầu Tiên Của Bạn Trong 5 Phút
- Cách Giải reCAPTCHA v2 Bằng API: Hướng Dẫn Từng Bước
- Cách giải Cloudflare Turnstile bằng API
- Cách giải quyết GeeTest v3 bằng API