عندما يصطدم خط أنابيب الاختبار لديك بنموذج تسجيل دخول محمي بـ reCAPTCHA v2، يتوقف الاختبار الآلي عند خطوة التحقق وينتظر تدخلاً بشرياً — فينكسر التشغيل ويتحوّل الاختبار الليلي إلى مهمة يدوية. الحل هو التعامل مع اختبار CAPTCHA برمجياً داخل بيئة تملكها أو مصرّح لك باختبارها، بحيث تكتمل الرحلة كاملة دون توقّف. يوفّر لك CaptchaAI هذه القدرة عبر API واحد يمكن استدعاؤه من داخل Pytest أو Selenium أو أي إطار اختبار آخر.
هذا الدليل موجّه لمهندسي ضمان الجودة الذين يختبرون تدفقات مملوكة أو بيئات تدريج (staging) خاصة بهم، ويشرح كيف تُبنى مساعدة اختبار قابلة لإعادة الاستخدام، وكيف تدمجها في اختبارات التكامل والاختبارات الشاملة، وكيف تضبط التكلفة داخل خط أنابيب CI.
متى تحتاج فرق ضمان الجودة إلى حل اختبار CAPTCHA برمجياً
اختبار CAPTCHA اليدوي لا يتوسّع مع فرق تشغّل عشرات المسارات يومياً. الجدول التالي يوضح السيناريوهات التي يصبح فيها الحل الآلي ضرورياً:
| السيناريو | لماذا يفيد CaptchaAI |
|---|---|
| اختبار الانحدار | التأكد من أن نماذج الدخول والتسجيل ما زالت تعمل بعد كل عملية نشر |
| الاختبار الشامل من طرف إلى طرف | تنفيذ رحلة المستخدم كاملة بما فيها خطوة التحقق |
| اختبار الحمل | محاكاة تدفقات محمية بـ CAPTCHA على نطاق واسع دون تدخل يدوي |
| الاختبار عبر المتصفحات | التأكد من ظهور widget التحقق بشكل صحيح في مختلف المتصفحات |
| اختبار إمكانية الوصول | التحقق من المسارات البديلة لذوي الاحتياجات الخاصة |
تخيّل فريق ضمان جودة في منصة تجارة إلكترونية بالخليج يستعد لموسم الجمعة البيضاء: يحتاج قبل الإطلاق إلى تشغيل مئات عمليات التحقق على نماذج التسجيل وإتمام الشراء في بيئة التدريج الخاصة بالمنصة. حلّ هذه النماذج يدوياً في كل دورة اختبار غير عملي، بينما يجعل استدعاء API من داخل مجموعة الاختبارات المسار كاملاً وقابلاً للتكرار خلال دقائق.
قبل أن تبدأ: نطاق الاختبار المصرّح به
هذا المسار مخصّص للتدفقات التي تملكها أو لديك إذن صريح باختبارها. تندرج ضمن النطاق المصرّح به الحالات التالية:
- بيئة تدريج (staging) داخلية غير متاحة للجمهور.
- حساب اختبار أنشأته على منصتك الخاصة.
- نموذج منشور على نطاق تملكه أو تديره.
أما تشغيل الأداة على مواقع الغير دون تفويض فهو خارج هذا النطاق تماماً. الالتزام بهذه الحدود يبقي الاختبار ضمن ضمان الجودة المعتمد ويجعل نتائجه قابلة للتدقيق لاحقاً. لتوثيق من شغّل الاختبارات ومتى، راجع تسجيل عمليات الحل للامتثال والتدقيق.
التكامل مع Pytest
ابدأ بمساعدة اختبار (helper) واحدة تلخّص دورة الحل في أربع خطوات:
- أرسل نوع reCAPTCHA v2 مع
sitekeyوعنوان الصفحة إلى نقطة النهايةin.php. - احفظ معرّف المهمة العائد في الاستجابة.
- استطلع
res.phpدورياً حتى تصبح النتيجة جاهزة. - مرّر الرمز العائد في حقل
g-recaptcha-responseضمن حمولة الطلب.
عرّف المساعدة كـ fixture على مستوى الجلسة حتى تشاركها كل الاختبارات، واقرأ مفتاح الـ API من متغير بيئة بدل تضمينه في الشيفرة. لاحظ كيف يتحقق الاختبار الأخير من أن النموذج يرفض الإرسال عند غياب الرمز — وهي حالة لا تقلّ أهمية عن حالة النجاح:
import pytest
import requests
import time
class CaptchaTestHelper:
"""Helper for solving CAPTCHAs in test environments."""
def __init__(self, api_key):
self.api_key = api_key
def solve_recaptcha(self, sitekey, pageurl, timeout=120):
"""Solve reCAPTCHA and return token."""
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
}, timeout=30)
result = resp.json()
assert result.get("status") == 1, f"Submit failed: {result}"
task_id = result["request"]
deadline = time.time() + timeout
time.sleep(10)
while time.time() < deadline:
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {data['request']}")
time.sleep(5)
raise TimeoutError("CAPTCHA solve timeout")
@pytest.fixture(scope="session")
def captcha_helper():
"""Provide CaptchaAI helper for test session."""
import os
api_key = os.environ.get("CAPTCHAAI_API_KEY")
if not api_key:
pytest.skip("CAPTCHAAI_API_KEY not set")
return CaptchaTestHelper(api_key)
class TestLoginFlow:
"""Test login flow behind reCAPTCHA."""
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
LOGIN_URL = "https://staging.example.com/login"
def test_login_with_valid_credentials(self, captcha_helper):
"""Verify login succeeds with valid creds and solved CAPTCHA."""
token = captcha_helper.solve_recaptcha(self.SITEKEY, self.LOGIN_URL)
assert token and len(token) > 100
resp = requests.post(self.LOGIN_URL, data={
"username": "test_user",
"password": "test_pass",
"g-recaptcha-response": token,
})
assert resp.status_code == 200
assert "Welcome" in resp.text
def test_login_with_invalid_credentials(self, captcha_helper):
"""Verify login fails gracefully with bad creds but valid CAPTCHA."""
token = captcha_helper.solve_recaptcha(self.SITEKEY, self.LOGIN_URL)
resp = requests.post(self.LOGIN_URL, data={
"username": "wrong_user",
"password": "wrong_pass",
"g-recaptcha-response": token,
})
assert resp.status_code in (200, 401)
assert "Invalid" in resp.text or "error" in resp.text.lower()
def test_login_without_captcha_fails(self):
"""Verify login rejects submissions without CAPTCHA."""
resp = requests.post(self.LOGIN_URL, data={
"username": "test_user",
"password": "test_pass",
})
assert resp.status_code in (400, 403, 422)
الرمز العائد هو قيمة الحقل g-recaptcha-response نفسها التي يتوقعها الخادم، لذا يكفي تمريره ضمن حمولة الطلب كما لو أن مستخدماً حقيقياً أكمل التحقق.
نمط الاختبار الشامل عبر Selenium
في الاختبارات الشاملة تحتاج إلى قيادة متصفح حقيقي: املأ الحقول، استخرج sitekey من عنصر الصفحة، أرسله إلى المساعدة السابقة، ثم احقن الرمز في الحقل المخفي واستدعِ دالة رد النداء (callback) إذا كان النموذج يعتمد عليها. هذا النمط يعيد استخدام نفس fixture الخاصة بالمساعدة، فيبقى منطق الحل في مكان واحد:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.fixture
def browser():
"""Create browser for testing."""
options = webdriver.ChromeOptions()
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
yield driver
driver.quit()
class TestRegistrationFlow:
"""Test registration form with CAPTCHA."""
REG_URL = "https://staging.example.com/register"
def test_registration_form_submits(self, browser, captcha_helper):
"""Full registration flow with CAPTCHA solving."""
browser.get(self.REG_URL)
# Fill form
browser.find_element(By.ID, "email").send_keys("test@example.com")
browser.find_element(By.ID, "password").send_keys("SecurePass123!")
browser.find_element(By.ID, "confirm_password").send_keys("SecurePass123!")
# Extract sitekey from page
captcha_div = browser.find_element(By.CSS_SELECTOR, ".g-recaptcha")
sitekey = captcha_div.get_attribute("data-sitekey")
# Solve via API
token = captcha_helper.solve_recaptcha(sitekey, browser.current_url)
# Inject token
browser.execute_script("""
document.querySelector('[name="g-recaptcha-response"]').value = arguments[0];
""", token)
# Trigger callback if needed
callback = captcha_div.get_attribute("data-callback")
if callback:
browser.execute_script(f"window['{callback}'](arguments[0]);", token)
# Submit
browser.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
# Verify success
WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".success-message"))
)
def test_captcha_renders_on_page(self, browser):
"""Verify CAPTCHA widget loads on registration page."""
browser.get(self.REG_URL)
captcha = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".g-recaptcha, iframe[src*='recaptcha']"))
)
assert captcha.is_displayed()
انتبه إلى استخراج sitekey من الصفحة نفسها في كل تشغيل بدل تثبيته في الشيفرة؛ فهذا يجعل الاختبار صامداً عند تغيير النموذج بين البيئات.
ضبط تشغيل اختبارات CAPTCHA في Pytest
اختبارات الحل أبطأ من بقية المجموعة، لذا افصلها بعلامة (marker) مخصّصة حتى تتحكم في وقت تشغيلها. عرّف العلامة في conftest.py وسجّلها في إعدادات المشروع:
# conftest.py
import os
# Mark tests that need CAPTCHA solving
def pytest_configure(config):
config.addinivalue_line(
"markers", "captcha: tests requiring CAPTCHA solving (may be slow)"
)
# pytest.ini or pyproject.toml
"""
[tool.pytest.ini_options]
markers = [
"captcha: tests requiring CAPTCHA solving (may be slow)",
]
"""
لتشغيل اختبارات CAPTCHA وحدها — مثلاً في وظيفة ليلية مجدولة:
pytest -m captcha -v
ولتشغيل خط أنابيب سريع يتجاوز خطوة الحل أثناء التطوير اليومي:
pytest -m "not captcha" -v
هذا الفصل يبقي دورة التغذية الراجعة للمطوّرين سريعة، ويحصر استدعاءات API في التشغيلات التي تحتاجها فعلاً.
نصائح لخفض تكلفة اختبارات CAPTCHA
بما أن CaptchaAI يعتمد التسعير على عدد الـ Threads المتزامنة لا على كل عملية حل، فإن ضبط توقيت الاختبارات وتجميعها هو مفتاح التحكم في التكلفة:
| الاستراتيجية | الفائدة |
|---|---|
| الاختبار في بيئة التدريج (staging) | صعوبة تحقق أقل ونتائج أسرع |
| تشغيل اختبارات CAPTCHA وفق جدول زمني بدل كل دفعة (push) | خفض عدد الطلبات المتزامنة |
| تخزين نتائج الاختبارات غير المستقرة مؤقتاً | تفادي إعادة الحل غير الضرورية |
| استخدام علامة بيئة لتخطي التحقق محلياً | توفير أثناء التطوير المحلي |
| تجميع اختبارات CAPTCHA في وظيفة CI مخصّصة | ضبط عدد الـ Threads لكل خط أنابيب |
عند التخطيط لسعة تشغيل أعلى، تساعدك أنماط معمارية لحل CAPTCHA على نطاق واسع على مواءمة عدد الـ Threads مع حجم مجموعة الاختبارات.
الأسئلة الشائعة
هل يُعدّ حل اختبار CAPTCHA في مجموعة الاختبارات استخداماً مشروعاً؟
نعم، طالما كان على تدفقات تملكها أو لديك إذن صريح باختبارها — بيئة تدريج داخلية أو حساب اختبار على منصتك. هذا هو جوهر اختبار ضمان الجودة المعتمد، ويختلف تماماً عن تشغيل الأداة على مواقع الغير دون تفويض.
ما الخطة المناسبة لفريق ضمان جودة يشغّل الاختبارات في CI؟
يعتمد الاختيار على عدد الاختبارات المتزامنة لا على إجماليها، لأن الفوترة على الـ Threads. تكفي خطة BASIC ($15 شهرياً، 5 Threads) لمجموعة اختبار صغيرة تعمل بالتسلسل، بينما تناسب STANDARD ($30 شهرياً، 15 Thread) خطوط الأنابيب التي تشغّل عدة اختبارات على التوازي. كل خطة تشمل عمليات حل غير محدودة ضمن الشهر.
كيف أتفادى هشاشة الاختبارات (flaky tests) عند الاعتماد على الحل الآلي؟
اضبط مهلة انتهاء (timeout) واضحة كما في المساعدة أعلاه، واستطلع النتيجة دورياً بدل انتظار ثابت، وخزّن نتائج الاختبارات غير المستقرة مؤقتاً لتفادي إعادة الحل. الفصل بعلامة captcha يمنع أيضاً تأثّر بقية المجموعة ببطء هذه الاختبارات.
هل يدعم CaptchaAI أنواع التحقق المستخدمة في نماذجنا؟
يحلّ CaptchaAI reCAPTCHA v2 وv3 وCloudflare Turnstile وChallenge وGeeTest v3 إضافة إلى تحديات الصور وOCR، وهي الأنواع الأكثر شيوعاً في نماذج الدخول والتسجيل. تأكد من مطابقة نوع التحقق في نموذجك لطريقة الإرسال الصحيحة قبل إدراجه في الاختبارات.
أدلة ذات صلة
- أتمتة خط أنابيب ضمان الجودة يبدأ بحساب واحد — أضف CaptchaAI إلى اختباراتك.*