Los sistemas de registro de cursos universitarios utilizan reCAPTCHA v2 para proteger los formularios de inscripción, las páginas de búsqueda de clases y los planificadores de horarios. Los equipos de control de calidad que prueban estos sistemas necesitan manejar CAPTCHA en múltiples escenarios de prueba: flujos de trabajo de inscripción, lógica de lista de espera, disponibilidad de asientos y cumplimiento de fechas límite. A continuación se explica cómo integrar la resolución de CAPTCHA en las pruebas de registro.
Dónde aparecen los CAPTCHA en los sistemas de registro
| Paso de registro | tipo CAPTCHA | Propósito |
|---|---|---|
| Página de inicio de sesión del estudiante | reCAPTCHA v2 | Prevenir el relleno de credenciales |
| Búsqueda de cursos/browse | reCAPTCHA v2 | Limitar las búsquedas automáticas |
| Cursos Add/drop | reCAPTCHA v2 | Evitar la inscripción automática |
| Registro en lista de espera | reCAPTCHA v2 | Bloquear bots de registro automático |
| Planificador de horarios | Cloudflare Turnstile | Proteger herramientas interactivas |
| Solicitudes de transcripción | reCAPTCHA v2 | Verificar usuario humano |
Prueba de registro con resolución CAPTCHA
import requests
import time
import re
class RegistrationTester:
def __init__(self, api_key, portal_url):
self.api_key = api_key
self.portal_url = portal_url
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def test_login(self, student_id, password):
"""Test login flow with CAPTCHA handling."""
login_page = self.session.get(f"{self.portal_url}/login")
site_key = self._extract_site_key(login_page.text)
token = self._solve_recaptcha(site_key, f"{self.portal_url}/login")
response = self.session.post(f"{self.portal_url}/login", data={
"studentId": student_id,
"password": password,
"g-recaptcha-response": token
})
return {
"status": response.status_code,
"redirected": response.url != f"{self.portal_url}/login",
"success": "dashboard" in response.url or response.status_code == 200
}
def test_course_search(self, term, department, level=None):
"""Test course search returns correct results."""
params = {"term": term, "dept": department}
if level:
params["level"] = level
response = self.session.get(
f"{self.portal_url}/courses/search", params=params
)
if self._has_captcha(response.text):
site_key = self._extract_site_key(response.text)
token = self._solve_recaptcha(
site_key, f"{self.portal_url}/courses/search"
)
response = self.session.post(
f"{self.portal_url}/courses/search",
data={**params, "g-recaptcha-response": token}
)
courses = self._parse_courses(response.text)
return {
"count": len(courses),
"courses": courses,
"all_match_dept": all(
c["department"] == department for c in courses
)
}
def test_enrollment(self, course_id, section):
"""Test enrolling in a course section."""
enroll_page = self.session.get(
f"{self.portal_url}/enroll/{course_id}/{section}"
)
if self._has_captcha(enroll_page.text):
site_key = self._extract_site_key(enroll_page.text)
token = self._solve_recaptcha(
site_key,
f"{self.portal_url}/enroll/{course_id}/{section}"
)
else:
token = None
form_data = {
"courseId": course_id,
"section": section,
"confirm": "true"
}
if token:
form_data["g-recaptcha-response"] = token
response = self.session.post(
f"{self.portal_url}/enroll/submit",
data=form_data
)
return {
"status": response.status_code,
"enrolled": "success" in response.text.lower(),
"waitlisted": "waitlist" in response.text.lower(),
"error": self._extract_error(response.text)
}
def test_seat_availability(self, course_ids):
"""Test seat availability display for multiple courses."""
results = []
for course_id in course_ids:
response = self.session.get(
f"{self.portal_url}/courses/{course_id}"
)
if self._has_captcha(response.text):
site_key = self._extract_site_key(response.text)
token = self._solve_recaptcha(
site_key, f"{self.portal_url}/courses/{course_id}"
)
response = self.session.post(
f"{self.portal_url}/courses/{course_id}",
data={"g-recaptcha-response": token}
)
availability = self._parse_availability(response.text)
results.append({"course_id": course_id, **availability})
return results
def _has_captcha(self, html):
return "g-recaptcha" in html or "recaptcha" in html.lower()
def _extract_site_key(self, html):
match = re.search(r'data-sitekey="([^"]+)"', html)
if match:
return match.group(1)
raise ValueError("reCAPTCHA site key not found")
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("reCAPTCHA solve timed out")
def _parse_courses(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_empty(node):
return node.text.strip() if node and node.text else ""
courses = []
for row in soup.select(".course-row, tr.course"):
courses.append({
"id": row.get("data-course-id", ""),
"department": text_or_empty(row.select_one(".dept")),
"number": text_or_empty(row.select_one(".number")),
"title": text_or_empty(row.select_one(".title")),
"seats": text_or_empty(row.select_one(".seats"))
})
return courses
def _parse_availability(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"total_seats": text_or_none(soup.select_one(".total-seats")),
"enrolled": text_or_none(soup.select_one(".enrolled-count")),
"available": text_or_none(soup.select_one(".available-seats")),
"waitlist": text_or_none(soup.select_one(".waitlist-count"))
}
def _extract_error(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
error = soup.select_one(".error-message, .alert-danger")
return error.text.strip() if error else None
Ejecutor del conjunto de pruebas (JavaScript)
class RegistrationTestSuite {
constructor(apiKey, portalUrl) {
this.apiKey = apiKey;
this.portalUrl = portalUrl;
this.results = [];
}
async runAll(testCredentials) {
const tests = [
() => this.testLogin(testCredentials),
() => this.testCourseSearch('Fall2025', 'CS'),
() => this.testEnrollment('CS101', '001'),
() => this.testDropCourse('CS101', '001'),
() => this.testWaitlistFlow('CS201', '001')
];
for (const test of tests) {
try {
const result = await test();
this.results.push({ ...result, passed: true });
} catch (error) {
this.results.push({ name: test.name, error: error.message, passed: false });
}
}
return this.generateReport();
}
async solveRecaptcha(pageUrl, html) {
const match = html.match(/data-sitekey="([^"]+)"/);
if (!match) return null;
const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: this.apiKey,
method: 'userrecaptcha',
googlekey: match[1],
pageurl: pageUrl,
json: '1'
})
});
const { request: taskId } = await submitResp.json();
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 3000));
const result = await fetch(
`https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
);
const data = await result.json();
if (data.status === 1) return data.request;
}
throw new Error('reCAPTCHA solve timed out');
}
generateReport() {
const passed = this.results.filter(r => r.passed).length;
const failed = this.results.filter(r => !r.passed).length;
return {
total: this.results.length,
passed,
failed,
passRate: `${((passed / this.results.length) * 100).toFixed(1)}%`,
details: this.results
};
}
}
// Usage
const suite = new RegistrationTestSuite('YOUR_API_KEY', 'https://register.university.edu');
const report = await suite.runAll({ studentId: 'test123', password: 'testpass' });
console.log(`Tests: ${report.passed}/${report.total} passed (${report.passRate})`);
Escenarios de prueba que activan CAPTCHA
| Escenario de prueba | Frecuencia CAPTCHA | Impacto |
|---|---|---|
| Iniciar sesión con varias cuentas | Alto | Cada intento de inicio de sesión activa CAPTCHA |
| Pruebas de inscripción simultánea | Alto | Protección anti-bots |
| Búsquedas rápidas de cursos | moderado | Consultas con tarifa limitada |
| Generación de horarios | Bajo | Interacción de sesión única |
| Validación de requisitos previos | Bajo | Parte del flujo de inscripción |
Solución de problemas
| Problema | causa | Solución |
|---|---|---|
| CAPTCHA en cada inicio de sesión de prueba | Sin reutilización de sesiones | Autenticarse una vez, ejecutar pruebas dentro de la sesión |
| La inscripción falla después de CAPTCHA | El token CSRF expiró | Extraiga el token CSRF nuevo con CAPTCHA |
| Los datos de prueba se restablecen entre ejecuciones | El portal borra las inscripciones para exámenes | Utilice un entorno de prueba dedicado |
| No se encontró la clave del sitio reCAPTCHA | SPA representa CAPTCHA dinámicamente | Utilice la automatización del navegador para acceder al DOM renderizado |
Preguntas frecuentes
¿Debo utilizar un entorno de prueba sin CAPTCHA?
Idealmente, los entornos de prueba deshabilitan los CAPTCHA. Cuando eso no es posible (o al probar el comportamiento de producción), CaptchaAI maneja la resolución de reCAPTCHA v2 para que las pruebas se ejecuten sin intervención manual.
¿Cómo manejo los CAPTCHA en los canales de prueba CI/CD?
Agregue CaptchaAI como dependencia de prueba. Cuando una prueba encuentre un CAPTCHA, resuélvalo a través de la API y continúe. Esto mantiene la confiabilidad de las pruebas de un extremo a otro sin deshabilitar las funciones de seguridad.
¿Puedo probar el registro bajo carga con CAPTCHA?
Sí. CaptchaAI maneja la resolución paralela de reCAPTCHA para pruebas de carga. Envíe varias solicitudes de resolución simultáneamente para cada sesión de estudiante simulada.
Artículos relacionados
- Cómo resolver la devolución de llamada de Recaptcha V2 usando Api
- Torniquete Recaptcha V2 Manejo en el mismo sitio
- Mecanismo de devolución de llamada Recaptcha V2
Próximos pasos
Pruebe los sistemas de registro de un extremo a otro:obtenga su clave API CaptchaAIy maneje los CAPTCHA del portal universitario en su conjunto de pruebas.