Tutorials

Pytest Fixtures for CaptchaAI API Testing

CAPTCHA-solving code touches external APIs, proxies, and browser interactions — it needs tests. But hitting the CaptchaAI API on every test run is slow and costs credits. Pytest fixtures let you mock the API for unit tests and run live integration tests only when needed.

This tutorial builds a reusable fixture library for CaptchaAI testing.


Project structure

tests/
├── conftest.py           # Shared fixtures
├── test_solver.py        # Unit tests (mocked)
├── test_integration.py   # Live API tests (optional)
└── captcha_client.py     # Your CaptchaAI client

What you need

Requirement Details
Python 3.8+ With pytest, requests, responses
CaptchaAI API key For integration tests only
pip install pytest requests responses

CaptchaAI client module

# captcha_client.py
import requests
import time


class CaptchaClient:
    BASE_URL = "https://ocr.captchaai.com"

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

    def submit(self, params):
        """Submit a CAPTCHA task and return the task ID."""
        params["key"] = self.api_key
        params["json"] = 1
        resp = requests.post(f"{self.BASE_URL}/in.php", data=params)
        resp.raise_for_status()
        data = resp.json()
        if data.get("status") != 1:
            raise RuntimeError(f"Submit failed: {data.get('request')}")
        return data["request"]

    def poll(self, task_id, timeout=120, interval=5):
        """Poll for the result of a submitted task."""
        deadline = time.time() + timeout
        while time.time() < deadline:
            resp = requests.get(f"{self.BASE_URL}/res.php", params={
                "key": self.api_key, "action": "get", "id": task_id, "json": 1
            })
            resp.raise_for_status()
            data = resp.json()
            if data.get("status") == 1:
                return data["request"]
            if data.get("request") != "CAPCHA_NOT_READY":
                raise RuntimeError(f"Solve failed: {data['request']}")
            time.sleep(interval)
        raise TimeoutError(f"Task {task_id} timed out after {timeout}s")

    def solve(self, params, timeout=120):
        """Submit and poll in one call."""
        task_id = self.submit(params)
        return self.poll(task_id, timeout=timeout)

    def balance(self):
        """Check account balance."""
        resp = requests.get(f"{self.BASE_URL}/res.php", params={
            "key": self.api_key, "action": "getbalance", "json": 1
        })
        resp.raise_for_status()
        return float(resp.json().get("request", 0))

Shared fixtures — conftest.py

# tests/conftest.py
import os
import pytest
import responses
from captcha_client import CaptchaClient


@pytest.fixture
def api_key():
    """Return test API key — use env var for live tests."""
    return os.getenv("CAPTCHAAI_API_KEY", "TEST_KEY_000000")


@pytest.fixture
def client(api_key):
    """CaptchaClient instance."""
    return CaptchaClient(api_key)


@pytest.fixture
def mock_api():
    """Activate responses mock for CaptchaAI endpoints."""
    with responses.RequestsMock() as rsps:
        yield rsps


@pytest.fixture
def mock_successful_solve(mock_api):
    """Mock a successful submit + poll cycle."""
    mock_api.add(
        responses.POST,
        "https://ocr.captchaai.com/in.php",
        json={"status": 1, "request": "12345"},
        status=200
    )
    mock_api.add(
        responses.GET,
        "https://ocr.captchaai.com/res.php",
        json={"status": 1, "request": "SOLVED_TOKEN_abc123"},
        status=200
    )
    return mock_api


@pytest.fixture
def mock_pending_then_solved(mock_api):
    """Mock a submit, then pending, then solved cycle."""
    mock_api.add(
        responses.POST,
        "https://ocr.captchaai.com/in.php",
        json={"status": 1, "request": "12345"},
        status=200
    )
    # First poll: not ready
    mock_api.add(
        responses.GET,
        "https://ocr.captchaai.com/res.php",
        json={"status": 0, "request": "CAPCHA_NOT_READY"},
        status=200
    )
    # Second poll: solved
    mock_api.add(
        responses.GET,
        "https://ocr.captchaai.com/res.php",
        json={"status": 1, "request": "SOLVED_TOKEN_xyz789"},
        status=200
    )
    return mock_api


@pytest.fixture
def mock_submit_error(mock_api):
    """Mock a submit error (e.g., wrong API key)."""
    mock_api.add(
        responses.POST,
        "https://ocr.captchaai.com/in.php",
        json={"status": 0, "request": "ERROR_WRONG_USER_KEY"},
        status=200
    )
    return mock_api


@pytest.fixture
def recaptcha_params():
    """Standard reCAPTCHA v2 parameters."""
    return {
        "method": "userrecaptcha",
        "googlekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
        "pageurl": "https://example.com/login"
    }


@pytest.fixture
def turnstile_params():
    """Standard Cloudflare Turnstile parameters."""
    return {
        "method": "turnstile",
        "sitekey": "0x4AAAAAAADnPIDROz1234",
        "pageurl": "https://example.com/protected"
    }

Unit tests — mocked API

# tests/test_solver.py
import pytest


def test_solve_returns_token(client, mock_successful_solve, recaptcha_params):
    """Successful solve returns a token string."""
    token = client.solve(recaptcha_params, timeout=10)
    assert token == "SOLVED_TOKEN_abc123"
    assert len(token) > 0


def test_solve_handles_pending(client, mock_pending_then_solved, recaptcha_params):
    """Client retries when task is not ready."""
    token = client.solve(recaptcha_params, timeout=30)
    assert token == "SOLVED_TOKEN_xyz789"


def test_submit_error_raises(client, mock_submit_error, recaptcha_params):
    """Submit errors raise RuntimeError."""
    with pytest.raises(RuntimeError, match="ERROR_WRONG_USER_KEY"):
        client.solve(recaptcha_params)


def test_submit_sends_correct_params(client, mock_successful_solve, recaptcha_params):
    """Verify the correct parameters are sent to in.php."""
    client.solve(recaptcha_params, timeout=10)
    body = mock_successful_solve.calls[0].request.body
    assert "userrecaptcha" in body
    assert "googlekey" in body


def test_balance_returns_float(client, mock_api):
    """Balance check returns a float value."""
    mock_api.add(
        "GET",
        "https://ocr.captchaai.com/res.php",
        json={"status": 1, "request": "12.50"},
        status=200
    )
    balance = client.balance()
    assert isinstance(balance, float)
    assert balance == 12.50

Integration tests — live API

# tests/test_integration.py
import os
import pytest

pytestmark = pytest.mark.skipif(
    not os.getenv("CAPTCHAAI_API_KEY"),
    reason="CAPTCHAAI_API_KEY not set — skipping live tests"
)


def test_live_balance(client):
    """Verify balance check works against the real API."""
    balance = client.balance()
    assert isinstance(balance, float)
    assert balance >= 0


def test_live_submit_recaptcha(client, recaptcha_params):
    """Submit a reCAPTCHA task to the live API."""
    task_id = client.submit(recaptcha_params)
    assert task_id.isdigit()

Run mocked tests:

pytest tests/test_solver.py -v

Run live tests:

CAPTCHAAI_API_KEY=your_key pytest tests/test_integration.py -v

Expected output (mocked):

tests/test_solver.py::test_solve_returns_token PASSED
tests/test_solver.py::test_solve_handles_pending PASSED
tests/test_solver.py::test_submit_error_raises PASSED
tests/test_solver.py::test_submit_sends_correct_params PASSED
tests/test_solver.py::test_balance_returns_float PASSED

Troubleshooting

Issue Cause Fix
ConnectionError in mocked tests responses not activated Use the mock_api fixture or @responses.activate
Live tests skipped CAPTCHAAI_API_KEY not set Export the env variable before running
TimeoutError in live tests Solve took too long Increase timeout parameter
Mock returns wrong response Response order matters responses serves mocks in FIFO order

FAQ

Should I mock or use live tests?

Mock for unit tests (fast, free, deterministic). Use live tests only for smoke tests in CI or integration validation. Mark live tests with pytest.mark.skipif so they don't run unless the API key is set.

How do I test error handling?

Create fixtures for each error code — ERROR_WRONG_USER_KEY, ERROR_ZERO_BALANCE, ERROR_NO_SLOT_AVAILABLE. Assert that your client raises the correct exception.

Can I use these fixtures for other CAPTCHA types?

Yes. Add parameter fixtures for each CAPTCHA type (GeeTest, image/OCR, Cloudflare Challenge). The solver logic is the same — only the submit parameters change.


Get your CaptchaAI API key

Start testing your CAPTCHA-solving workflows at captchaai.com. These fixtures work with every CAPTCHA type CaptchaAI supports.


Discussions (0)

No comments yet.

Related Posts

Use Cases Multi-Step Checkout Automation with CAPTCHA Solving
Automate multi-step e-commerce checkout flows that include CAPTCHA challenges at cart, payment, or confirmation stages using Captcha AI.

Automate multi-step e-commerce checkout flows that include CAPTCHA challenges at cart, payment, or confirmatio...

Python Automation Cloudflare Turnstile
Mar 21, 2026
Troubleshooting CaptchaAI Wrong CAPTCHA Type Error: How to Fix
Fix wrong CAPTCHA type errors when using Captcha AI.

Fix wrong CAPTCHA type errors when using Captcha AI. Learn how to identify the correct CAPTCHA type on a page...

Python Automation Cloudflare Turnstile
Feb 28, 2026
Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Python Automation Cloudflare Turnstile
Apr 08, 2026
Troubleshooting ERROR_PAGEURL: URL Mismatch Troubleshooting Guide
Fix ERROR_PAGEURL when using Captcha AI.

Fix ERROR_PAGEURL when using Captcha AI. Diagnose URL mismatch issues, handle redirects, SPAs, and dynamic URL...

Python Automation Cloudflare Turnstile
Mar 23, 2026
Tutorials CAPTCHA Solving Fallback Chains
Implement fallback chains for CAPTCHA solving with Captcha AI.

Implement fallback chains for CAPTCHA solving with Captcha AI. Cascade through solver methods, proxy pools, an...

Python Automation Cloudflare Turnstile
Apr 06, 2026
Troubleshooting Handling reCAPTCHA v2 and Cloudflare Turnstile on the Same Site
Solve both re CAPTCHA v 2 and Cloudflare Turnstile on sites that use multiple CAPTCHA providers — detect which type appears, solve each correctly, and handle pr...

Solve both re CAPTCHA v 2 and Cloudflare Turnstile on sites that use multiple CAPTCHA providers — detect which...

Python Automation Cloudflare Turnstile
Mar 23, 2026
Tutorials Build an Automated Testing Pipeline with CaptchaAI
Build a CI/CD testing pipeline that uses Captcha AI to handle CAPTCHAs in end-to-end tests.

Build a CI/CD testing pipeline that uses Captcha AI to handle CAPTCHAs in end-to-end tests. Covers pytest inte...

Python Automation reCAPTCHA v2
Jan 16, 2026
Use Cases Multi-Step Workflow Automation with CaptchaAI
Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at scale.

Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at sc...

Python Automation Cloudflare Turnstile
Apr 06, 2026
Integrations Solving CAPTCHAs in React Native WebViews with CaptchaAI
how to detect and solve re CAPTCHA v 2 and Cloudflare Turnstile CAPTCHAs inside React Native Web Views using the Captcha AI API with working Java Script bridge...

Learn how to detect and solve re CAPTCHA v 2 and Cloudflare Turnstile CAPTCHAs inside React Native Web Views u...

Python Automation Cloudflare Turnstile
Mar 30, 2026
Use Cases CAPTCHA Handling in Continuous Integration Testing
Integrate CAPTCHA solving into CI/CD pipelines — run end-to-end tests against CAPTCHA-protected pages in Git Hub Actions, Git Lab CI, and Jenkins with Captcha A...

Integrate CAPTCHA solving into CI/CD pipelines — run end-to-end tests against CAPTCHA-protected pages in Git H...

Python Cloudflare Turnstile reCAPTCHA v2
Mar 28, 2026
Tutorials Handling Multiple CAPTCHAs on a Single Page
how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI.

Learn how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI. Covers multi-iframe ext...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 09, 2026
Tutorials Streaming Batch Results: Processing CAPTCHA Solutions as They Arrive
Process CAPTCHA solutions the moment they arrive instead of waiting for tasks to complete — use async generators, event emitters, and callback patterns for stre...

Process CAPTCHA solutions the moment they arrive instead of waiting for all tasks to complete — use async gene...

Python Automation All CAPTCHA Types
Apr 07, 2026