Flaky CAPTCHA-Tests haben in verteilten QA-Läufen fast immer dieselbe Ursache: Die Worker starten aus unterschiedlichen Zuständen. Die Lösung ist kein „geteilter Live-Kontext", sondern ein pro Testlauf dokumentierter Sitzungszustand — Testkonto, Cookies und Storage-Snapshot, die einer bekannten Testlauf-ID zugeordnet sind. Wer diesen Startzustand explizit ablegt und wieder einliest, vergleicht in jedem Worker denselben Testfall statt zufälliger Bedingungen. Dieser Leitfaden zeigt das Muster in drei Schritten mit Redis und CaptchaAI.
Anwendungsbereich: Dieser Leitfaden gilt für eigene oder autorisierte QA-Umgebungen. Er zeigt, wie verteilte Testworker Browser- und Anwendungskontext reproduzierbar koordinieren. Er beschreibt keine Sitzungsübernahme auf fremden Websites und keine Optimierung für unautorisierte Zugriffe.
Warum reproduzierbarer Sitzungszustand über Worker hinweg zählt
Verteilte Testworker — etwa parallele GitLab-CI-Runner oder mehrere Hetzner-VMs — prüfen dieselbe Funktion oft unter verschiedenen Bedingungen:
- Erstbesuch eines frischen Testkontos
- bestehende Sitzung mit aktivem Feature-Flag
- abgelaufene Session als Negativtest
- parallele Regression für mehrere Rollen
Ohne abgestimmten Startzustand vergleichen Sie keine identischen Szenarien. Ein Worker beginnt mit leeren Cookies, der nächste mit gespeichertem Zustand, ein dritter mit abweichenden Testdaten — und am Ende stehen CAPTCHA-Fehler im Protokoll, die in Wahrheit aus ungleichen Startbedingungen stammen und nicht aus der Anwendung. Genau diese Fehldiagnosen kosten in verteilten QA-Läufen die meiste Zeit.
Welche Zustandsdaten zwischen QA-Workern geteilt werden dürfen
In QA gehört nur das in den gemeinsamen Speicher, was zur Reproduzierbarkeit eines Testfalls beiträgt. Produktive Nutzerdaten haben dort nichts zu suchen.
| Zustandsart | Darf zwischen QA-Workern geteilt werden? | Hinweis |
|---|---|---|
| Testlauf-ID | Ja | Verbindet Logs, Screenshots und Backend-Antworten |
| Testkonto-Metadaten | Ja | Rolle, Mandant, Feature-Flags |
| Cookie-Snapshot einer Staging-Sitzung | Ja, wenn Teil des Testfalls | Vor dem Lauf dokumentieren und versionieren |
| Local-Storage-Snapshot | Ja, wenn Teil des Testfalls | Für reproduzierbare UI-Zustände |
| Ergebnisprotokoll eines CAPTCHA-Laufs | Ja | Für Diagnose und Berichte |
| Produktive Nutzerdaten | Nein | Nie in QA-Artefakte übernehmen |
Diese Trennung hält nachvollziehbar, warum ein Worker einen bestimmten Zustand verwendet und welches Artefakt zu welchem Testfall gehört. Für DACH-Teams ist sie zugleich eine DSGVO-Frage: Sobald echte personenbezogene Daten in Test-Snapshots landen, wird aus einem Testartefakt eine Verarbeitung mit eigener Rechtsgrundlage. In QA arbeiten Sie deshalb ausschließlich mit synthetischen Testkonten.
Schritt 1 — Sitzungs-Snapshots in Redis ablegen
Der erste Schritt legt den Startzustand jedes Workers unter einem eindeutigen Schlüssel aus Testlauf-ID und Worker-ID ab. Damit ist jederzeit rekonstruierbar, mit welchen Cookies und welchem Storage ein Worker in den Lauf gegangen ist.
from __future__ import annotations
import json
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
def session_key(test_run_id: str, worker_id: str) -> str:
return f"qa:session:{test_run_id}:{worker_id}"
def save_session_snapshot(*, test_run_id: str, worker_id: str, cookies: dict, storage: dict) -> None:
payload = {
"test_run_id": test_run_id,
"worker_id": worker_id,
"cookies": cookies,
"storage": storage,
}
r.set(session_key(test_run_id, worker_id), json.dumps(payload), ex=3600)
def load_session_snapshot(test_run_id: str, worker_id: str) -> dict:
raw = r.get(session_key(test_run_id, worker_id))
if not raw:
return {"cookies": {}, "storage": {}}
return json.loads(raw)
Entscheidend ist hier nicht die Redis-Technik selbst, sondern die Struktur: Jeder Worker erhält klar definierte Sitzungsdaten, die einem bekannten QA-Lauf zugeordnet sind. Das ex=3600 begrenzt die Lebensdauer des Snapshots — nach dem Lauf verfällt er automatisch und hinterlässt keine Altlasten.
Schritt 2 — CAPTCHA-Prüfung pro Worker mit CaptchaAI ausführen
Sobald Startzustand und Testlauf-ID feststehen, durchläuft jeder Worker denselben Verifikationspfad. CaptchaAI übernimmt dabei die eigentliche CAPTCHA-Prüfung: Sitekey und Page-URL werden übermittelt, anschließend fragt der Worker das Ergebnis ab. Unterstützt sind unter anderem reCAPTCHA v2 und v3, Cloudflare Turnstile, GeeTest v3 sowie Bild- und Raster-CAPTCHAs.
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_for_worker(page_url: str, sitekey: str) -> str:
submit = requests.post(
"https://ocr.captchaai.com/in.php",
data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": 1,
},
timeout=30,
)
submit.raise_for_status()
submit_json = submit.json()
if submit_json.get("status") != 1:
raise RuntimeError(submit_json.get("request", "submit failed"))
task_id = submit_json["request"]
for _ in range(30):
time.sleep(5)
result = requests.get(
"https://ocr.captchaai.com/res.php",
params={"key": API_KEY, "action": "get", "id": task_id, "json": 1},
timeout=30,
)
result.raise_for_status()
result_json = result.json()
if result_json.get("status") == 1:
return result_json["request"]
raise TimeoutError("CaptchaAI result timed out for QA worker")
Auch das ist ein QA-Schritt gegen Ihre eigene Umgebung. Das zurückgegebene Token wird mit Testlauf, Worker-ID und erwarteter Antwort korreliert — nicht gehortet: Ein gelöstes reCAPTCHA-Token bleibt nur rund 120 Sekunden gültig, es muss also unmittelbar vor der Übermittlung im Formular verwendet werden.
Für die Kapazitätsplanung hilft ein Blick auf das Abrechnungsmodell: CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung — ein Thread entspricht einer parallel laufenden Prüfung. Der BASIC-Plan (15 $/Monat, 5 Threads) trägt damit fünf parallele QA-Worker, STANDARD (30 $/Monat, 15 Threads) deckt größere CI-Matrizen ab. Innerhalb eines Plans sind die Lösungen pro Thread unbegrenzt. Preise in US-Dollar.
Schritt 3 — Backend-Antwort pro Worker verifizieren
Der letzte Schritt schließt die Schleife: Der Worker schickt Testlauf-ID, Worker-ID und das gelöste Token an Ihr eigenes Staging-Backend und prüft dessen Antwort.
def verify_worker_run(*, test_run_id: str, worker_id: str, token: str) -> dict:
response = requests.post(
"https://staging.example-app.test/qa/captcha/verify",
json={
"testRunId": test_run_id,
"workerId": worker_id,
"token": token,
"environment": "staging",
},
timeout=30,
)
response.raise_for_status()
return response.json()
So lässt sich pro Worker nachvollziehen:
- welcher Startzustand aktiv war,
- welche Antwort Ihr Backend geliefert hat,
- ob Unterschiede zwischen parallelen Läufen auf den Zustand oder auf die Anwendung zurückgehen.
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Zwei Worker liefern unterschiedliche Resultate | Unterschiedlicher Sitzungsstart | Start-Snapshots pro Worker explizit dokumentieren |
| Ergebnisse lassen sich nicht korrelieren | Testlauf-ID fehlt | Testlauf-ID in Browser, Backend und Redis gemeinsam verwenden |
| Ein Worker startet mit leerem Zustand | Snapshot wurde nicht geladen | Redis-Lookup vor Testbeginn prüfen |
| Backend akzeptiert nur einzelne Läufe | Testdaten überschneiden sich | Eindeutige Testkonten und eindeutige Worker-IDs verwenden |
| Fehler treten nur unter Parallelität auf | Gemeinsame Fixtures werden verändert | Fixtures schreibgeschützt halten oder pro Worker klonen |
Häufige Fragen
Wie stelle ich sicher, dass parallele Worker denselben Testfall prüfen?
Über eine gemeinsame Testlauf-ID plus dokumentierten Start-Snapshot. Jeder Worker liest vor dem Lauf denselben Zustand aus Redis ein und schreibt sein Ergebnis unter derselben ID zurück — erst dadurch werden parallele Läufe vergleichbar statt zufällig.
Welche CAPTCHA-Typen deckt CaptchaAI in solchen QA-Läufen ab?
reCAPTCHA v2 und v3 (inklusive Enterprise), Cloudflare Turnstile und Challenge, GeeTest v3 sowie Bild-, OCR- und Raster-CAPTCHAs. CaptchaFox, Friendly Captcha und Lemin befinden sich in Beta. hCaptcha, FunCaptcha und GeeTest v4 werden derzeit nicht unterstützt.
Dürfen echte Produktivdaten in verteilte QA-Snapshots?
Nein. In QA arbeiten Sie ausschließlich mit synthetischen Testkonten. Echte personenbezogene Daten in Test-Snapshots sind aus DSGVO-Sicht eine eigene Verarbeitung — und für reproduzierbare Tests ohnehin unnötig.
Wie lange bleibt ein gelöstes CAPTCHA-Token gültig?
Ein reCAPTCHA-Token bleibt rund 120 Sekunden gültig. Speichern Sie es nicht auf Vorrat, sondern verwenden Sie es direkt im Verifikationsschritt. Für die TTL-Steuerung solcher kurzlebigen Werte in Redis lohnt ein Blick auf die passende Token-TTL-Diagnose.
Weiterführende Leitfäden
- CaptchaAI in wenigen Minuten einrichten
- CAPTCHA-QA in autorisierten Testumgebungen
- Redis für die CAPTCHA-Token-TTL-Diagnose in QA-Workflows
- CAPTCHA-Endpoints in eigenen Webformularen testen
Koordinieren Sie verteilte QA-Worker mit klaren Zustandsdaten und reproduzierbaren Verifikationsläufen – CaptchaAI unterstützt belastbare CAPTCHA-Tests in eigenen Umgebungen.