Integrations

Kiểm tra CAPTCHA trên Android với Espresso và CaptchaAI

Các thử nghiệm giao diện người dùng Android được xây dựng bằng Espresso thường gặp CAPTCHA bên trong WebView - trang đăng nhập, biểu mẫu đăng ký hoặc luồng thanh toán được nhúng hiển thị reCAPTCHA v2.CaptchaAIcung cấp giải pháp theo chương trình để bộ kiểm tra tự động của bạn có thể chạy từ đầu đến cuối mà không cần tương tác CAPTCHA thủ công.

Hướng dẫn này trình bày cách phát hiện CAPTCHA trong Android WebView trong quá trình kiểm tra Espresso, giải quyết chúng thông qua dịch vụ phụ trợ và đưa lại mã thông báo vào trang.

Kịch bản thế giới thực

Ứng dụng Android của bạn tải trang thanh toán của bên thứ ba trong WebView. Trang trình bày reCAPTCHA v2 trước khi cho phép thanh toán. Trong quá trình kiểm tra thiết bị của Espresso, CAPTCHA này sẽ chặn kiểm tra xác minh thanh toán.

Môi trường: Android Studio, Kotlin, Espresso, AndroidX Test, API CaptchaAI, chương trình phụ trợ Python.

Bước 1: Tạo Trình trợ giúp kiểm tra trong ứng dụng

Thêm trình trợ giúp chỉ gỡ lỗi có thể đánh giá JavaScript bên trong WebView của ứng dụng:

// CaptchaTestHelper.kt — debug source set only
package com.example.app.testing

import android.webkit.JavascriptInterface
import android.webkit.WebView
import kotlinx.coroutines.*
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject

class CaptchaTestHelper(private val webView: WebView) {

    private var detectedSitekey: String? = null
    private var detectedPageUrl: String? = null
    private var solvedToken: String? = null

    @JavascriptInterface
    fun onCaptchaDetected(sitekey: String, pageurl: String) {
        detectedSitekey = sitekey
        detectedPageUrl = pageurl
    }

    fun detectCaptcha() {
        webView.post {
            webView.evaluateJavascript("""
                (function() {
                    var el = document.querySelector('.g-recaptcha');
                    if (el) {
                        CaptchaHelper.onCaptchaDetected(
                            el.getAttribute('data-sitekey'),
                            window.location.href
                        );
                        return 'found';
                    }
                    return 'not_found';
                })();
            """, null)
        }
    }

    suspend fun solveAndInject(): Boolean = withContext(Dispatchers.IO) {
        val sitekey = detectedSitekey ?: return@withContext false
        val pageurl = detectedPageUrl ?: return@withContext false

        // Call backend solver
        val client = OkHttpClient.Builder()
            .callTimeout(java.time.Duration.ofMinutes(3))
            .build()

        val body = JSONObject().apply {
            put("captchaType", "recaptcha_v2")
            put("sitekey", sitekey)
            put("pageurl", pageurl)
        }.toString().toRequestBody("application/json".toMediaType())

        val request = Request.Builder()
            .url("http://10.0.2.2:3000/api/solve-captcha")  // Host loopback for emulator
            .post(body)
            .build()

        val response = client.newCall(request).execute()
        val json = JSONObject(response.body?.string() ?: "")
        val token = json.optString("token", "")

        if (token.isEmpty()) return@withContext false

        solvedToken = token

        // Inject token on main thread
        withContext(Dispatchers.Main) {
            webView.evaluateJavascript("""
                document.getElementById('g-recaptcha-response').value = '$token';
                try {
                    var clients = ___grecaptcha_cfg.clients;
                    Object.keys(clients).forEach(function(k) {
                        Object.keys(clients[k]).forEach(function(j) {
                            if (clients[k][j] && clients[k][j].callback) {
                                clients[k][j].callback('$token');
                            }
                        });
                    });
                } catch(e) {}
            """, null)
        }

        return@withContext true
    }

    companion object {
        fun attach(webView: WebView): CaptchaTestHelper {
            val helper = CaptchaTestHelper(webView)
            webView.addJavascriptInterface(helper, "CaptchaHelper")
            return helper
        }
    }
}

Bước 2: Dịch vụ bộ giải phụ trợ

Chạy bộ giải Python này trên máy phát triển của bạn trong quá trình thực hiện kiểm tra:

# android_test_solver.py
import os
import time
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
API_KEY = os.environ.get("CAPTCHAAI_API_KEY", "YOUR_API_KEY")

@app.route("/api/solve-captcha", methods=["POST"])
def solve():
    data = request.json

    # Submit to CaptchaAI
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": data["sitekey"],
        "pageurl": data["pageurl"],
        "json": "1",
    })
    result = resp.json()
    if result.get("status") != 1:
        return jsonify({"error": result.get("request")}), 400

    task_id = result["request"]

    # Poll for result
    for _ in range(30):
        time.sleep(5)
        poll = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": "1",
        })
        poll_result = poll.json()
        if poll_result.get("status") == 1:
            return jsonify({"token": poll_result["request"]})
        if poll_result.get("request") != "CAPCHA_NOT_READY":
            return jsonify({"error": poll_result["request"]}), 400

    return jsonify({"error": "Timeout"}), 408

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=3000)

Bước 3: Kiểm tra Espresso bằng cách xử lý CAPTCHA

// CheckoutCaptchaTest.kt
package com.example.app

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class CheckoutCaptchaTest {

    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    @Test
    fun testCheckoutWithCaptcha() {
        // Navigate to checkout
        onView(withId(R.id.checkout_button)).perform(click())

        // Wait for WebView to load
        Thread.sleep(5000)

        // Access the WebView and attach helper
        activityRule.scenario.onActivity { activity ->
            val webView = activity.findViewById<android.webkit.WebView>(R.id.webview)

            val helper = CaptchaTestHelper.attach(webView)
            helper.detectCaptcha()

            // Wait for detection
            Thread.sleep(2000)

            // Solve and inject
            runBlocking {
                val solved = helper.solveAndInject()
                assert(solved) { "CAPTCHA should be solved successfully" }
            }
        }

        // Continue with form submission after token injection
        Thread.sleep(1000)

        // Verify checkout completed
        onView(withText("Order Confirmed")).check(
            androidx.test.espresso.assertion.ViewAssertions.matches(isDisplayed())
        )
    }
}

Khắc phục sự cố

Vấn đề Nguyên nhân Cách xử lý
10.0.2.2 không thể truy cập được Không sử dụng Trình giả lập Android Sử dụng IP máy chủ thực tế cho các thiết bị vật lý; 10.0.2.2 dành riêng cho trình giả lập
Cuộc gọi lại evaluateJavascript là null WebView chưa được tải đầy đủ Thêm trình nghe WebViewClient.onPageFinished() trước khi đánh giá
addJavascriptInterface không hoạt động Đã tắt JavaScript Gọi webView.settings.javaScriptEnabled = true
Yêu cầu mạng bị chặn bởi chính sách Cleartext HTTP sang localhost trên Android 9+ Thêm android:usesCleartextTraffic="true" vào AndroidManifest.xml (chỉ gỡ lỗi)

Câu hỏi thường gặp

Espresso có thể tương tác trực tiếp với nội dung WebView không?

Espresso có onWebView() cho các tương tác WebView cơ bản nhưng không thể đánh giá JavaScript tùy ý. Bạn cần evaluateJavascript() từ API WebView để xử lý CAPTCHA.

Tính năng này có hoạt động trên các thiết bị thực dành cho CI không?

Vâng. Thay thế 10.0.2.2 bằng IP thực của máy đang chạy chương trình phụ trợ bộ giải. Đảm bảo thiết bị có thể tiếp cận phần phụ trợ qua mạng.

Làm cách nào để ngăn người trợ giúp kiểm tra chuyển sang sản xuất?

Đặt các trình trợ giúp kiểm tra vào bộ nguồn src/debug/java/. Các biến thể bản dựng Android tự động loại trừ các nguồn gỡ lỗi khỏi bản dựng bản phát hành.

Còn reCAPTCHA Enterprise trong ứng dụng Android thì sao?

Cách tiếp cận tương tự nhưng bạn cần có khóa trang Doanh nghiệp và có thể cần chuyển các tham số bổ sung như enterprise: 1 cho CaptchaAI.

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ây dựng quy trình thử nghiệm tự động Captchaai
  • Xử lý cửa quay Recaptcha V2 trên cùng một trang web

Các bước tiếp theo

Tự động kiểm tra CAPTCHA trên Android của bạn —lấy khóa API CaptchaAI của bạnvà thiết lập phần phụ trợ của bộ giải.

Hướng dẫn liên quan:

Os comentários estão desativados para este artigo.

Postagens relacionadas

Tutorials Xây dựng quy trình thử nghiệm tự động với CaptchaAI
Hướng dẫn từng bước để xây dựng quy trình thử nghiệm tự động với Captcha AI, với các ví dụ có thể sử dụng lại trực tiếp và quy trình làm việc Captcha AI rõ ràng...

Hướng dẫn từng bước để xây dựng quy trình thử nghiệm tự động với Captcha AI, với các ví dụ có thể sử dụng lại...

Apr 30, 2026
Use Cases Gửi biểu mẫu tự động với xử lý CAPTCHA
Hướng dẫn thực hành về Gửi biểu mẫu tự động với quy trình xử lý CAPTCHA, với các tình huống thực tế, lời khuyên về quy trình làm việc và các bước có thể thực hi...

Hướng dẫn thực hành về Gửi biểu mẫu tự động với quy trình xử lý CAPTCHA, với các tình huống thực tế, lời khuyê...

Apr 24, 2026
Use Cases Xử lý CAPTCHA trong thử nghiệm tích hợp liên tục
Hướng dẫn thực hành Xử lý CAPTCHA trong thử nghiệm tích hợp liên tục, với các tình huống thực tế, lời khuyên về quy trình làm việc và các bước có thể thực hiện...

Hướng dẫn thực hành Xử lý CAPTCHA trong thử nghiệm tích hợp liên tục, với các tình huống thực tế, lời khuyên v...

May 04, 2026