Use Cases

注册流程测试中的验证码处理

测试用户注册流程端到端 - 即使验证码阻止您的测试自动化。


为什么注册测试需要验证码解决

注册表单几乎总是受到验证码保护,以防止机器人帐户。质量检查团队需要:

  • 验证注册流程在暂存和生产中是否有效
  • 测试边缘情况(重复的电子邮件、弱密码、缺失字段)
  • 对每个部署运行回归测试
  • 验证验证码放置不会破坏表单

测试架构

┌──────────┐     ┌───────────────┐     ┌────────────┐     ┌────────────┐
│ Test Data │────▶│ Fill Form +   │────▶│ Solve      │────▶│ Verify     │
│ Generator │     │ Edge Cases    │     │ CAPTCHA    │     │ Account    │
└──────────┘     └───────────────┘     └────────────┘     └────────────┘

执行

测试数据生成器

import random
import string
import time


class TestUser:
    def __init__(self, prefix="test"):
        ts = int(time.time())
        rand = ''.join(random.choices(string.ascii_lowercase, k=4))
        self.first_name = f"{prefix}_{rand}"
        self.last_name = "User"
        self.email = f"{prefix}_{ts}_{rand}@testmail.example.com"
        self.username = f"{prefix}_{ts}_{rand}"
        self.password = f"Test!{ts}{rand.upper()}"

    def as_dict(self):
        return {
            "first_name": self.first_name,
            "last_name": self.last_name,
            "email": self.email,
            "username": self.username,
            "password": self.password,
        }

注册测试员

import time
import requests
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


class CaptchaSolver:
    BASE = "https://ocr.captchaai.com"

    def __init__(self, api_key):
        self.api_key = api_key

    def solve_recaptcha(self, sitekey, pageurl):
        return self._solve({
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
        })

    def solve_turnstile(self, sitekey, pageurl):
        return self._solve({
            "method": "turnstile",
            "sitekey": sitekey,
            "pageurl": pageurl,
        })

    def _solve(self, params, initial_wait=10):
        params["key"] = self.api_key
        params["json"] = 1
        resp = requests.post(f"{self.BASE}/in.php", data=params).json()
        if resp["status"] != 1:
            raise Exception(resp["request"])
        task_id = resp["request"]
        time.sleep(initial_wait)
        for _ in range(60):
            result = requests.get(
                f"{self.BASE}/res.php",
                params={"key": self.api_key, "action": "get", "id": task_id, "json": 1},
            ).json()
            if result["request"] == "CAPCHA_NOT_READY":
                time.sleep(5)
                continue
            if result["status"] == 1:
                return result["request"]
            raise Exception(result["request"])
        raise TimeoutError("Timed out")


class RegistrationTester:
    def __init__(self, api_key, base_url):
        self.solver = CaptchaSolver(api_key)
        self.base_url = base_url
        self.driver = webdriver.Chrome()
        self.wait = WebDriverWait(self.driver, 10)
        self.results = []

    def _fill(self, selector, value):
        el = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
        el.clear()
        el.send_keys(value)

    def _solve_captcha(self):
        html = self.driver.page_source
        page_url = self.driver.current_url

        # Turnstile
        turnstile = self.driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
        if turnstile:
            sitekey = turnstile[0].get_attribute("data-sitekey")
            token = self.solver.solve_turnstile(sitekey, page_url)
            self.driver.execute_script(
                f'document.querySelector("[name=cf-turnstile-response]").value = "{token}";'
            )
            return

        # reCAPTCHA
        recaptcha = self.driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
        if recaptcha and "recaptcha" in html.lower():
            sitekey = recaptcha[0].get_attribute("data-sitekey")
            token = self.solver.solve_recaptcha(sitekey, page_url)
            self.driver.execute_script(
                f'document.querySelector("[name=g-recaptcha-response]").value = "{token}";'
            )

    def _get_errors(self):
        """Collect any visible error messages on the page."""
        error_selectors = [
            ".error", ".alert-danger", ".form-error",
            "[role='alert']", ".validation-error",
        ]
        errors = []
        for sel in error_selectors:
            for el in self.driver.find_elements(By.CSS_SELECTOR, sel):
                text = el.text.strip()
                if text:
                    errors.append(text)
        return errors

    def _check_success(self):
        """Check if registration succeeded."""
        html = self.driver.page_source.lower()
        url = self.driver.current_url.lower()
        success_indicators = [
            "welcome", "account created", "verify your email",
            "registration successful", "thank you for registering",
        ]
        return any(ind in html or ind in url for ind in success_indicators)

    # --- Test Cases ---

    def test_valid_registration(self):
        """Test: Valid registration should succeed."""
        user = TestUser()
        self.driver.get(f"{self.base_url}/register")

        self._fill("[name='firstName'], #first-name", user.first_name)
        self._fill("[name='lastName'], #last-name", user.last_name)
        self._fill("[name='email'], #email", user.email)
        self._fill("[name='username'], #username", user.username)
        self._fill("[name='password'], #password", user.password)

        confirm_fields = self.driver.find_elements(By.CSS_SELECTOR, "[name='confirmPassword'], #confirm-password")
        if confirm_fields:
            confirm_fields[0].clear()
            confirm_fields[0].send_keys(user.password)

        self._solve_captcha()

        self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
        time.sleep(3)

        success = self._check_success()
        self.results.append({
            "test": "valid_registration",
            "passed": success,
            "user": user.email,
            "errors": self._get_errors() if not success else [],
        })
        return success

    def test_duplicate_email(self):
        """Test: Duplicate email should show error."""
        user = TestUser()

        # First registration
        self.driver.get(f"{self.base_url}/register")
        self._fill("[name='email'], #email", user.email)
        self._fill("[name='password'], #password", user.password)
        self._fill("[name='firstName'], #first-name", user.first_name)
        self._solve_captcha()
        self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
        time.sleep(3)

        # Second registration with same email
        self.driver.get(f"{self.base_url}/register")
        self._fill("[name='email'], #email", user.email)
        self._fill("[name='password'], #password", user.password)
        self._fill("[name='firstName'], #first-name", "Duplicate")
        self._solve_captcha()
        self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
        time.sleep(3)

        errors = self._get_errors()
        has_error = len(errors) > 0 or not self._check_success()
        self.results.append({
            "test": "duplicate_email",
            "passed": has_error,
            "errors": errors,
        })
        return has_error

    def test_weak_password(self):
        """Test: Weak password should be rejected."""
        user = TestUser()
        self.driver.get(f"{self.base_url}/register")

        self._fill("[name='email'], #email", user.email)
        self._fill("[name='password'], #password", "123")  # Weak password
        self._fill("[name='firstName'], #first-name", user.first_name)
        self._solve_captcha()
        self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
        time.sleep(3)

        errors = self._get_errors()
        rejected = len(errors) > 0 or not self._check_success()
        self.results.append({
            "test": "weak_password",
            "passed": rejected,
            "errors": errors,
        })
        return rejected

    def run_all(self):
        """Run all registration tests."""
        tests = [
            self.test_valid_registration,
            self.test_duplicate_email,
            self.test_weak_password,
        ]
        for test_fn in tests:
            try:
                test_fn()
            except Exception as e:
                self.results.append({
                    "test": test_fn.__name__,
                    "passed": False,
                    "errors": [str(e)],
                })
        return self.results

    def report(self):
        passed = sum(1 for r in self.results if r["passed"])
        total = len(self.results)
        lines = [f"Registration Tests: {passed}/{total} passed", "-" * 40]
        for r in self.results:
            status = "PASS" if r["passed"] else "FAIL"
            lines.append(f"  [{status}] {r['test']}")
            if r.get("errors"):
                for err in r["errors"]:
                    lines.append(f"         {err}")
        return "\n".join(lines)

    def close(self):
        self.driver.quit()

用法

tester = RegistrationTester("YOUR_API_KEY", "https://staging.example.com")

try:
    tester.run_all()
    print(tester.report())
finally:
    tester.close()

输出:

Registration Tests: 3/3 passed
----------------------------------------
  [PASS] valid_registration
  [PASS] duplicate_email
  [PASS] weak_password

与 pytest 集成

import pytest


@pytest.fixture(scope="module")
def tester():
    t = RegistrationTester("YOUR_API_KEY", "https://staging.example.com")
    yield t
    t.close()


def test_valid_registration(tester):
    assert tester.test_valid_registration(), "Valid registration should succeed"


def test_duplicate_email_rejected(tester):
    assert tester.test_duplicate_email(), "Duplicate email should be rejected"


def test_weak_password_rejected(tester):
    assert tester.test_weak_password(), "Weak password should be rejected"

故障排除

问题 原因 处理方式
注册成功但测试显示失败 成功指标不匹配 将您网站的成功文本添加到 _check_success()
未检测到验证码 验证码延迟后加载 _solve_captcha()之前添加time.sleep(2)
未找到字段 不同的 HTML 结构 更新您网站的 CSS 选择器
令牌已过期 解决得太早了 _solve_captcha() 移近以提交

常问问题

如何在不属于我的网站上测试注册?

本指南用于测试您自己的应用程序。仅在您有权测试的网站上自动注册。

我可以在 CI/CD 中运行这些测试吗?

是的。使用无头 Chrome (options.add_argument("--headless")) 并将 API 密钥设置为 CI 环境变量。

如何清理测试帐户?

添加一个拆卸步骤,通过管理 API 删除测试帐户,或使用命名约定 (test_*) 以便于识别和批量清理。


相关指南


在没有验证码拦截器的情况下测试注册流程 -使用CaptchaAI.

该文章已禁用评论。

相关文章

Use Cases 使用验证码处理自动提交表单
使用验证码处理自动提交表单的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

使用验证码处理自动提交表单的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

Apr 20, 2026
Use Cases 持续集成测试中的验证码处理
持续集成测试中的验证码处理实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

持续集成测试中的验证码处理实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

May 03, 2026
Use Cases Web 表单中 API 端点测试的 CAPTCHA 解决方案
Web 表单中 API 端点测试的 CAPTCHA 解决方案实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

Web 表单中 API 端点测试的 CAPTCHA 解决方案实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

May 07, 2026