Hệ thống đăng ký khóa học đại học sử dụng reCAPTCHA v2 để bảo vệ các biểu mẫu đăng ký, trang tìm kiếm lớp học và người lập kế hoạch lịch trình. Các nhóm QA kiểm tra các hệ thống này cần xử lý CAPTCHA trong nhiều tình huống kiểm tra — quy trình đăng ký, logic danh sách chờ, chỗ trống và thực thi thời hạn. Dưới đây là cách tích hợp giải pháp CAPTCHA vào kiểm tra đăng ký.
Nơi CAPTCHA xuất hiện trong hệ thống đăng ký
| Bước đăng ký | loại CAPTCHA | Mục đích |
|---|---|---|
| Trang đăng nhập sinh viên | reCAPTCHA v2 | Ngăn chặn việc nhồi thông tin xác thực |
| Tìm kiếm khóa học/browse | reCAPTCHA v2 | Hạn chế tra cứu tự động |
| Thêm các khóa học/drop | reCAPTCHA v2 | Ngăn chặn đăng ký tự động |
| Đăng ký danh sách chờ | reCAPTCHA v2 | Chặn bot đăng ký tự động |
| lập kế hoạch lịch trình | Cloudflare Turnstile | Bảo vệ các công cụ tương tác |
| Yêu cầu bảng điểm | reCAPTCHA v2 | Xác minh người dùng con người |
Kiểm tra đăng ký bằng giải 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
Bộ chạy thử nghiệm (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})`);
Các kịch bản thử nghiệm kích hoạt CAPTCHA
| Kịch bản thử nghiệm | tần số CAPTCHA | tác động |
|---|---|---|
| Đăng nhập bằng nhiều tài khoản | Cao | Mỗi lần đăng nhập sẽ kích hoạt CAPTCHA |
| Kỳ thi tuyển sinh đồng thời | Cao | Bảo vệ chống bot |
| Tìm kiếm khóa học nhanh chóng | Trung bình | Truy vấn có giới hạn tỷ lệ |
| Tạo lịch trình | Thấp | Tương tác một phiên |
| Xác thực điều kiện tiên quyết | Thấp | Một phần của quy trình tuyển sinh |
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| CAPTCHA trên mỗi lần đăng nhập thử nghiệm | Không tái sử dụng phiên | Xác thực một lần, chạy thử nghiệm trong phiên |
| Đăng ký không thành công sau CAPTCHA | Mã thông báo CSRF đã hết hạn | Trích xuất mã thông báo CSRF mới bằng CAPTCHA |
| Kiểm tra dữ liệu đặt lại giữa các lần chạy | Cổng thông tin xóa đăng ký kiểm tra | Sử dụng môi trường thử nghiệm chuyên dụng |
| Không tìm thấy khóa trang web reCAPTCHA | SPA hiển thị CAPTCHA một cách linh hoạt | Sử dụng tự động hóa trình duyệt để truy cập DOM được hiển thị |
Câu hỏi thường gặp
Tôi có nên sử dụng môi trường thử nghiệm không có CAPTCHA không?
Lý tưởng nhất là môi trường dàn dựng sẽ vô hiệu hóa CAPTCHA. Khi điều đó là không thể (hoặc khi kiểm tra hành vi sản xuất), CaptchaAI sẽ xử lý việc giải reCAPTCHA v2 để các bài kiểm tra chạy mà không cần can thiệp thủ công.
Làm cách nào để xử lý CAPTCHA trong quy trình thử nghiệm CI/CD?
Thêm CaptchaAI làm phần phụ thuộc thử nghiệm. Khi bài kiểm tra gặp CAPTCHA, hãy giải quyết nó thông qua API và tiếp tục. Điều này giúp cho các bài kiểm tra đầu cuối luôn đáng tin cậy mà không cần tắt các tính năng bảo mật.
Tôi có thể kiểm tra quá trình đăng ký bằng CAPTCHA không?
Vâng. CaptchaAI xử lý việc giải reCAPTCHA song song để kiểm tra tải. Gửi nhiều yêu cầu giải quyết đồng thời cho mỗi buổi học mô phỏng.
bài viết liên quan
- Cách giải quyết cuộc gọi lại Recaptcha V2 bằng Api
- Xử lý cửa quay Recaptcha V2 trên cùng một trang web
- Cơ chế gọi lại Recaptcha V2
Các bước tiếp theo
Hệ thống đăng ký thử nghiệm từ đầu đến cuối —lấy khóa API CaptchaAI của bạnvà xử lý CAPTCHA của cổng thông tin đại học trong bộ thử nghiệm của bạn.