اختبر نقاط النهاية المحمية بـ CAPTCHA بدون متصفح - قم بحل اختبارات CAPTCHA عبر واجهة برمجة التطبيقات وأرسلها مباشرة إلى نقاط النهاية الخلفية.
عندما تحتاج إلى هذا
- اختبار التحقق من صحة الخلفية: تأكد من أن الخادم يتحقق من صحة رموز CAPTCHA بشكل صحيح
- اختبار التحميل: قم بإرسال العديد من الطلبات إلى نقاط النهاية المحمية بـ CAPTCHA
- اختبار التكامل: اختبار واجهات برمجة التطبيقات لتقديم النماذج في CI/CD
- اختبار الاستجابة للخطأ: التحقق من رسائل الخطأ المناسبة للرموز المميزة غير الصالحة/expired
الهندسة المعمارية
┌──────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
│ Solve │────▶│ Build │────▶│ POST to │────▶│ Validate │
│ CAPTCHA │ │ Request │ │ Endpoint │ │ Response │
│ (API) │ │ Payload │ │ │ │ │
└──────────┘ └────────────┘ └──────────────┘ └──────────────┘
لا حاجة إلى متصفح لمعظم اختبارات نقطة النهاية.
التنفيذ
مزود رمز CAPTCHA
import time
import requests
class TokenProvider:
BASE = "https://ocr.captchaai.com"
def __init__(self, api_key):
self.api_key = api_key
def get_recaptcha_token(self, sitekey, pageurl, version="v2"):
params = {
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
}
if version == "v3":
params["version"] = "v3"
params["action"] = "submit"
return self._solve(params, initial_wait=15 if version == "v3" else 10)
def get_turnstile_token(self, sitekey, pageurl):
return self._solve({
"method": "turnstile",
"sitekey": sitekey,
"pageurl": pageurl,
})
def _solve(self, params, initial_wait=10):
params["key"] = self.api_key
params["json"] = 1
resp = requests.post(f"{self.BASE}/in.php", data=params).json()
if resp["status"] != 1:
raise Exception(resp["request"])
task_id = resp["request"]
time.sleep(initial_wait)
for _ in range(60):
result = requests.get(
f"{self.BASE}/res.php",
params={"key": self.api_key, "action": "get", "id": task_id, "json": 1},
).json()
if result["request"] == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result["status"] == 1:
return result["request"]
raise Exception(result["request"])
raise TimeoutError("Timed out")
اختبار نقطة النهاية
import json
import time
class EndpointTester:
def __init__(self, api_key):
self.token_provider = TokenProvider(api_key)
self.session = requests.Session()
self.results = []
def test_endpoint(self, config):
"""
config: {
"name": "test name",
"url": "endpoint URL",
"method": "POST",
"captcha_type": "recaptcha_v2" | "recaptcha_v3" | "turnstile",
"sitekey": "...",
"pageurl": "...",
"captcha_field": "g-recaptcha-response",
"payload": { ... form data ... },
"expected_status": 200,
"expected_contains": "success",
}
"""
start = time.time()
result = {"name": config["name"], "passed": False}
try:
# Get CAPTCHA token
captcha_type = config.get("captcha_type", "recaptcha_v2")
if captcha_type == "recaptcha_v2":
token = self.token_provider.get_recaptcha_token(
config["sitekey"], config["pageurl"]
)
elif captcha_type == "recaptcha_v3":
token = self.token_provider.get_recaptcha_token(
config["sitekey"], config["pageurl"], version="v3"
)
elif captcha_type == "turnstile":
token = self.token_provider.get_turnstile_token(
config["sitekey"], config["pageurl"]
)
else:
raise ValueError(f"Unknown captcha type: {captcha_type}")
# Build payload
payload = {**config.get("payload", {})}
captcha_field = config.get("captcha_field", "g-recaptcha-response")
payload[captcha_field] = token
# Submit request
method = config.get("method", "POST").upper()
headers = config.get("headers", {})
if config.get("json_body"):
resp = self.session.request(
method, config["url"], json=payload, headers=headers
)
else:
resp = self.session.request(
method, config["url"], data=payload, headers=headers
)
# Validate response
result["status_code"] = resp.status_code
result["response_length"] = len(resp.text)
result["elapsed"] = round(time.time() - start, 2)
# Check expected status
expected_status = config.get("expected_status", 200)
if resp.status_code != expected_status:
result["error"] = f"Expected {expected_status}, got {resp.status_code}"
self.results.append(result)
return result
# Check expected content
expected = config.get("expected_contains")
if expected and expected.lower() not in resp.text.lower():
result["error"] = f"Response missing: '{expected}'"
self.results.append(result)
return result
result["passed"] = True
except Exception as e:
result["error"] = str(e)
result["elapsed"] = round(time.time() - start, 2)
self.results.append(result)
return result
def test_invalid_token(self, config):
"""Test that endpoint rejects invalid CAPTCHA tokens."""
invalid_config = {**config}
invalid_config["name"] = f"{config['name']} (invalid token)"
# Override with fake token
payload = {**config.get("payload", {})}
captcha_field = config.get("captcha_field", "g-recaptcha-response")
payload[captcha_field] = "INVALID_TOKEN_12345"
start = time.time()
result = {"name": invalid_config["name"], "passed": False}
try:
resp = self.session.post(config["url"], data=payload)
result["status_code"] = resp.status_code
result["elapsed"] = round(time.time() - start, 2)
# Should reject — 4xx or error message
if resp.status_code >= 400 or "error" in resp.text.lower() or "invalid" in resp.text.lower():
result["passed"] = True
else:
result["error"] = "Endpoint accepted invalid CAPTCHA token"
except Exception as e:
result["error"] = str(e)
result["elapsed"] = round(time.time() - start, 2)
self.results.append(result)
return result
def test_missing_token(self, config):
"""Test that endpoint rejects missing CAPTCHA token."""
start = time.time()
result = {"name": f"{config['name']} (missing token)", "passed": False}
try:
payload = config.get("payload", {})
resp = self.session.post(config["url"], data=payload)
result["status_code"] = resp.status_code
result["elapsed"] = round(time.time() - start, 2)
if resp.status_code >= 400 or "captcha" in resp.text.lower():
result["passed"] = True
else:
result["error"] = "Endpoint accepted request without CAPTCHA"
except Exception as e:
result["error"] = str(e)
result["elapsed"] = round(time.time() - start, 2)
self.results.append(result)
return result
def run_suite(self, configs):
"""Run a full test suite against multiple endpoints."""
for config in configs:
self.test_endpoint(config)
self.test_invalid_token(config)
self.test_missing_token(config)
return self.report()
def report(self):
passed = sum(1 for r in self.results if r["passed"])
total = len(self.results)
lines = [f"Endpoint Tests: {passed}/{total} passed", "=" * 50]
for r in self.results:
status = "PASS" if r["passed"] else "FAIL"
elapsed = r.get("elapsed", "?")
lines.append(f" [{status}] {r['name']} ({elapsed}s)")
if r.get("error"):
lines.append(f" Error: {r['error']}")
return "\n".join(lines)
الاستخدام
tester = EndpointTester("YOUR_API_KEY")
configs = [
{
"name": "Contact form submission",
"url": "https://example.com/api/contact",
"captcha_type": "recaptcha_v2",
"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"pageurl": "https://example.com/contact",
"captcha_field": "g-recaptcha-response",
"payload": {
"name": "Test User",
"email": "test@example.com",
"message": "Automated test message",
},
"expected_status": 200,
"expected_contains": "success",
},
{
"name": "Newsletter signup",
"url": "https://example.com/api/subscribe",
"captcha_type": "turnstile",
"sitekey": "0x4AAAA...",
"pageurl": "https://example.com/newsletter",
"captcha_field": "cf-turnstile-response",
"payload": {
"email": "test@example.com",
},
"expected_status": 200,
},
]
report = tester.run_suite(configs)
print(report)
الإخراج:
Endpoint Tests: 5/6 passed
==================================================
[PASS] Contact form submission (18.5s)
[PASS] Contact form submission (invalid token) (0.3s)
[PASS] Contact form submission (missing token) (0.2s)
[PASS] Newsletter signup (14.2s)
[FAIL] Newsletter signup (invalid token) (0.3s)
Error: Endpoint accepted invalid CAPTCHA token
[PASS] Newsletter signup (missing token) (0.2s)
استكشاف الأخطاء وإصلاحها
| المشكلة | السبب | الإجراء |
|---|---|---|
| تم رفض الرمز المميز الصالح | انتهت صلاحية الرمز المميز قبل الإرسال | تقليل التأخير بين الحل والإرسال |
| تم قبول رمز مميز غير صالح | الواجهة الخلفية لا تتحقق من صحة اختبار CAPTCHA | خطأ في الملف - مشكلة أمنية |
| 403 على جميع الطلبات | رمز CSRF أو ملفات تعريف الارتباط مفقودة | أضف ملفات تعريف الارتباط للجلسة أو رأس CSRF |
| ترفض نقطة نهاية JSON بيانات النموذج | نوع محتوى خاطئ | قم بتعيين json_body: True في التكوين |
الأسئلة الشائعة
هل يمكنني اختبار نقاط النهاية دون حل اختبار CAPTCHA الحقيقي؟
بالنسبة لاختبارات الرمز المميزZ/missing غير الصالحة، ليست هناك حاجة إلى حل اختبار CAPTCHA - فقط أرسل بدون رمز مميز. لاختبارات الإرسال الصالحة، تحتاج إلى رمز مميز حقيقي من CaptchaAI.
كيف يمكنني اختبار نقاط النهاية ذات المعدل المحدود؟
أضف تأخيرًا بين الطلبات واختبرها بتكرار متزايد. تتبع متى تبدأ نقطة النهاية بإرجاع 429 ردًا.
هل يجب علي اختبار التحقق من صحة اختبار CAPTCHA في اختبارات الوحدة؟
محاكاة التحقق من صحة اختبار CAPTCHA في اختبارات الوحدة. استخدم هذا الأسلوب للتكامل والاختبارات الشاملة حيث تحتاج إلى رموز CAPTCHA حقيقية.
أدلة ذات صلة
- أتمتة إرسال النماذج مع التعامل مع CAPTCHA
- إنتاجية حل اختبار CAPTCHA: كيفية معالجة 10000 مهمة في الساعة
اختبر كل نقطة نهاية محمية بـ CAPTCHA –استخدم CaptchaAI.