Sistem pendaftaran kursus universitas menggunakan reCAPTCHA v2 untuk melindungi formulir pendaftaran, halaman pencarian kelas, dan perencana jadwal. Tim QA yang menguji sistem ini perlu menangani CAPTCHA di beberapa skenario pengujian – alur kerja pendaftaran, logika daftar tunggu, ketersediaan kursi, dan penegakan tenggat waktu. Berikut cara mengintegrasikan penyelesaian CAPTCHA ke dalam pengujian pendaftaran.
Dimana CAPTCHA Muncul di Sistem Registrasi
| Langkah pendaftaran | jenis CAPTCHA | Tujuan |
|---|---|---|
| Halaman login siswa | reCAPTCHA v2 | Cegah isian kredensial |
| Pencarian kursus/browse | reCAPTCHA v2 | Batasi pencarian otomatis |
| Tambahkan kursus /drop | reCAPTCHA v2 | Cegah pendaftaran otomatis |
| Pendaftaran daftar tunggu | reCAPTCHA v2 | Blokir bot pendaftaran otomatis |
| Perencana jadwal | Cloudflare Turnstile | Lindungi alat interaktif |
| Permintaan transkrip | reCAPTCHA v2 | Verifikasi pengguna manusia |
Pengujian Registrasi dengan Pemecahan 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
Pelari Rangkaian Tes (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})`);
Skenario Uji yang Memicu CAPTCHA
| Skenario pengujian | frekuensi CAPTCHA | Dampak |
|---|---|---|
| Masuk dengan banyak akun | Tinggi | Setiap upaya login memicu CAPTCHA |
| Tes pendaftaran bersamaan | Tinggi | Perlindungan anti-bot |
| Pencarian kursus cepat | Sedang | Kueri dengan tarif terbatas |
| Pembuatan jadwal | Rendah | Interaksi satu sesi |
| Validasi prasyarat | Rendah | Bagian dari alur pendaftaran |
Pemecahan masalah
| Masalah | Sebab | Perbaiki |
|---|---|---|
| CAPTCHA pada setiap login tes | Tidak ada penggunaan kembali sesi | Otentikasi sekali, jalankan tes dalam sesi |
| Pendaftaran gagal setelah CAPTCHA | Token CSRF sudah habis masa berlakunya | Ekstrak token CSRF baru dengan CAPTCHA |
| Data pengujian disetel ulang di antara proses yang berjalan | Portal menghapus pendaftaran tes | Gunakan lingkungan pengujian khusus |
| sitekey reCAPTCHA tidak ditemukan | SPA merender CAPTCHA secara dinamis | Gunakan otomatisasi browser untuk mengakses DOM yang dirender |
Pertanyaan Umum
Haruskah saya menggunakan lingkungan pengujian tanpa CAPTCHA?
Idealnya, lingkungan staging menonaktifkan CAPTCHA. Jika hal tersebut tidak memungkinkan (atau saat menguji perilaku produksi), CaptchaAI menangani penyelesaian reCAPTCHA v2 sehingga pengujian berjalan tanpa intervensi manual.
Bagaimana cara menangani CAPTCHA di Pipeline pengujian CI/CD?
Tambahkan CaptchaAI sebagai ketergantungan pengujian. Saat pengujian menemukan CAPTCHA, selesaikan melalui API dan lanjutkan. Hal ini membuat pengujian end-to-end tetap dapat diandalkan tanpa menonaktifkan fitur keamanan.
Bisakah saya menguji registrasi yang sedang dimuat dengan CAPTCHA?
Ya. CaptchaAI menangani penyelesaian reCAPTCHA paralel untuk pengujian beban. Kirimkan beberapa permintaan penyelesaian secara bersamaan untuk setiap sesi siswa yang disimulasikan.
Artikel Terkait
- Cara Mengatasi Callback Recaptcha V2 Menggunakan Api
- Penanganan Situs yang Sama Turnstile Recaptcha V2
- Mekanisme callback Recaptcha V2
Langkah Selanjutnya
Uji sistem registrasi secara end-to-end —dapatkan kunci API CaptchaAI Andadan menangani CAPTCHA portal universitas di ruang tes Anda.