99 0
Posted on 2026-8-26 13:42:21 | Show All Floors Reading Mode
Handling CAPTCHA in Playwright and Selenium Tests: The 2026 Playbook

If your end-to-end tests touch login, signup, checkout or any protected flow, you have hit the wall: the test runs, the form fills, and then a CAPTCHA stops everything. This guide shows the clean, legitimate way to handle CAPTCHA in Playwright and Selenium suites - solve it out-of-band with an API like OMOCaptcha and inject the token, instead of trying to click checkboxes like a human.

Why you should not click CAPTCHAs in tests

- Flaky by design. Interactive challenges are built to detect automation; fighting them makes suites brittle.
- Slow. Every challenge adds seconds of waiting; multiplied by hundreds of runs it destroys feedback time.
- Against the spirit of testing. You are testing your application, not the CAPTCHA vendor.

The professional pattern is token injection: ask a solving API for a token, then set it in the page exactly as the widget would.

The three-step pattern with OMOCaptcha

1. Create a task. Send the page URL and the widget sitekey to https://api.omocaptcha.com/v2/createTask (task type RecaptchaV2TokenTask for reCAPTCHA v2, HCaptchaTokenTask for hCaptcha, TurnstileTokenTask for Turnstile).
2. Poll getTaskResult until status is ready; read the token from solution.
3. Inject the token into the hidden response field and submit the form.

Playwright example (reCAPTCHA v2)

import asyncio, requests
from playwright.async_api import async_playwright

API_KEY = "YOUR_API_KEY"
BASE = "https://api.omocaptcha.com/v2"

def solve_recaptcha(page_url, sitekey):
create = requests.post(f"(BASE)/createTask", json=(
"clientKey": API_KEY,
"task": ("type": "RecaptchaV2TokenTask",
"websiteURL": page_url, "websiteKey": sitekey)
)).json()
assert create["errorId"] == 0
while True:
res = requests.post(f"(BASE)/getTaskResult", json=(
"clientKey": API_KEY, "taskId": create["taskId"])).json()
if res["status"] == "ready":
return res["solution"]["gRecaptchaResponse"]
assert res["status"] != "fail"
import time; time.sleep(2)

async def login_with_solved_captcha():
token = solve_recaptcha("https://your-app.example/login", "6Lc_SITEKEY")
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("https://your-app.example/login")
await page.fill("#email", "test-user@example.com")
await page.fill("#password", "secret")
# inject the solved token exactly where the widget writes it
await page.evaluate("""(t) => (
const el = document.querySelector('textarea[name="g-recaptcha-response"]')
- - document.createElement('textarea');
el.name = 'g-recaptcha-response'; el.value = t;
)""", token)
await page.click("button[type=submit]")
await page.wait_for_selector(".dashboard")

Selenium version (same idea)

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://your-app.example/login")
token = solve_recaptcha(driver.current_url, "6Lc_SITEKEY")
driver.execute_script("""(t) => (
let el = document.querySelector('textarea[name="g-recaptcha-response"]');
if (!el) ( el = document.createElement('textarea'); el.name = 'g-recaptcha-response';
document.querySelector('form').appendChild(el); )
el.value = t;
)""", token)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

hCaptcha works identically: the field name is h-captcha-response, and the token comes from solution.gRecaptchaResponse of an HCaptchaTokenTask. Full details in how to solve hCaptcha (https://blog.omocaptcha.com/how-to-solve-hcaptcha).

Test-environment best practices

- Use a dedicated test account and your own application's flows. Solving CAPTCHAs is for testing systems you own or are authorized to test - never for abusing third-party sites.
- Cache tokens briefly. Tokens live about two minutes; request them just before submit, not at suite start.
- Keep one user-agent. Use the same UA for solving and for the browser; mismatches look suspicious to validators.
- Budget for latency. Solves average 0.42s on OMOCaptcha (AI-only, no human queue), so a solve per run is affordable even in large suites.
- Watch your spend. Pricing starts from $0.27/1000 for reCAPTCHA-class solves; a 500-run suite costs pennies. See captcha solver API pricing (https://blog.omocaptcha.com/captcha-solver-api-pricing).

When the widget is invisible: reCAPTCHA v3

v3 returns a score instead of a challenge, and low scores silently block real users and tests alike. The fix is the same token-injection pattern with a freshly solved v3 token; see the dedicated reCAPTCHA v3 guide (https://blog.omocaptcha.com/how-to-solve-recaptcha).

Why OMOCaptcha for CI

- Predictable p99: AI-only means no human-queue tail latency wrecking suite duration
- 6 official SDKs (Python, JS/Node, PHP, Java, .NET, Go) match whatever your harness uses
- Refund SLA: full refund if success rate drops below 95%, so a bad day does not become a bill
- 1000 free solves on signup - enough to wire the pattern into your suite before paying anything

Get the quickstart (https://blog.omocaptcha.com/captcha-solver-api-quickstart), create your key at omocaptcha.com (https://omocaptcha.com/en?utm_source=blog&utm_medium=organic), and your next green build will not care which widget your product team shipped. Questions: support@omocaptcha.com, 24/7.
Omocaptcha - Gi?i CAPTCHA t? d?ng 1-3s, chính xác 99.2% - omocaptcha.com

For related infringement, reports, complaints, and suggestions, please send an email to: admin@discuz.vip

Powered by Discuz! X5.1 © 2001-2025 Discuz! Team.

InThis SectionPostBack to Top
Quick Reply Back to Top Return to List