XCUITest ile yapılan iOS uygulama testleri, genellikle yerleşik WKWebView'lerdeki CAPTCHA'lara (giriş formları, ödeme ağ geçitleri ve reCAPTCHA zorlukları sunan üçüncü taraf entegrasyonlarına) ulaşır.CaptchaAIbunları çözerek kullanıcı arayüzü testlerinizin manuel müdahale olmadan uçtan uca akışları tamamlayabilmesini sağlar.
Bu kılavuz, XCUITest çalıştırmaları sırasında WKWebView'deki CAPTCHA'ların nasıl tespit edileceğini, bunların yardımcı bir hizmetten CaptchaAI aracılığıyla nasıl çözüleceğini ve belirtecin web içeriğine geri nasıl enjekte edileceğini gösterir.
Gerçek Dünya Senaryosu
İOS uygulamanız WKWebView'da bir kayıt formu yükler. Form reCAPTCHA v2'yi içerir. Otomatik test sırasında bu CAPTCHA, testin ilerlemesini engeller. Şunları sağlayacak bir çözüme ihtiyacınız var:
- Test yürütme sırasında Web Görünümünde CAPTCHA'yı algılar
- Site anahtarını programlı olarak çıkarır
- CaptchaAI aracılığıyla çözer
- Formun gönderilebilmesi için jetonu enjekte eder
Ortam: Xcode 15+, Swift, XCUITest, macOS test çalıştırıcısı, CaptchaAI API.
Mimarlık
XCUITest, WKWebView'da doğrudan JavaScript çalıştıramaz. Yaklaşım, uygulamanın test sırasında çağırdığı bir yardımcı uç noktayı kullanır:
| Bileşen | Rol |
|---|---|
| XCUITest | Kullanıcı arayüzünü yönetir, test yardımcısı aracılığıyla CAPTCHA çözümünü tetikler |
| Test Yardımcısı API'si | Site anahtarını + URL'yi alır, CaptchaAI'yi çağırır, jetonu döndürür |
| Uygulama Testi Kancası | /inject'yi tespit etmek için WKWebView'deki JavaScript'i değerlendirir |
| CaptchaAI API'si | CAPTCHA sorununu çözer |
1. Adım: Uygulamaya bir Test Kancası ekleyin
Uygulamanızın WKWebView denetleyicisine, erişilebilirlik tanımlayıcıları veya URL şeması aracılığıyla tetiklenebilecek bir test modu CAPTCHA işleyicisi ekleyin:
// CaptchaTestHelper.swift — Add to app target (test build only)
import WebKit
#if DEBUG
class CaptchaTestHelper {
private let webView: WKWebView
init(webView: WKWebView) {
self.webView = webView
}
func detectCaptcha(completion: @escaping (String?, String?) -> Void) {
let script = """
(function() {
var el = document.querySelector('.g-recaptcha');
if (el) {
return JSON.stringify({
sitekey: el.getAttribute('data-sitekey'),
pageurl: window.location.href
});
}
return null;
})();
"""
webView.evaluateJavaScript(script) { result, error in
guard let jsonString = result as? String,
let data = jsonString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: String] else {
completion(nil, nil)
return
}
completion(json["sitekey"], json["pageurl"])
}
}
func injectToken(_ token: String, completion: @escaping (Bool) -> Void) {
let script = """
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) {}
true;
"""
webView.evaluateJavaScript(script) { _, error in
completion(error == nil)
}
}
func solveCaptchaViaBackend(
sitekey: String, pageurl: String,
completion: @escaping (Result<String, Error>) -> Void
) {
guard let url = URL(string: "http://localhost:3000/api/solve-captcha") else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: String] = [
"captchaType": "recaptcha_v2",
"sitekey": sitekey,
"pageurl": pageurl
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let token = json["token"] as? String else {
completion(.failure(NSError(domain: "", code: -1,
userInfo: [NSLocalizedDescriptionKey: "No token"])))
return
}
completion(.success(token))
}.resume()
}
}
#endif
Adım 2: Arka Uç Çözücü Hizmeti
Test sırasında CaptchaAI ile iletişim kuran yerel bir çözümleyici hizmetini çalıştırın:
# ios_test_solver.py — Run on test machine during XCUITest execution
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
sitekey = data["sitekey"]
pageurl = data["pageurl"]
# Submit to CaptchaAI
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
return jsonify({"error": result.get("request")}), 400
task_id = result["request"]
# Poll
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)
Adım 3: XCUITest Entegrasyonu
XCUITest'inizde, CAPTCHA içeren Web Görünümü yüklendiğinde CAPTCHA çözüm akışını tetikleyin:
// CaptchaUITests.swift
import XCTest
class CaptchaUITests: XCTestCase {
func testRegistrationWithCaptcha() throws {
let app = XCUIApplication()
app.launchArguments.append("--captcha-test-mode")
app.launch()
// Navigate to registration
app.buttons["Register"].tap()
// Wait for WebView to load
let webView = app.webViews.firstMatch
XCTAssertTrue(webView.waitForExistence(timeout: 15))
// Trigger CAPTCHA solve via test helper button
// (The app shows this button only in test mode)
let solveButton = app.buttons["SolveCaptchaTestHelper"]
if solveButton.waitForExistence(timeout: 5) {
solveButton.tap()
// Wait for solve completion indicator
let solved = app.staticTexts["CaptchaSolved"]
XCTAssertTrue(solved.waitForExistence(timeout: 120),
"CAPTCHA should be solved within 2 minutes")
}
// Continue with form submission
app.buttons["SubmitForm"].tap()
// Verify success
let success = app.staticTexts["Registration Complete"]
XCTAssertTrue(success.waitForExistence(timeout: 10))
}
}
Köprü sözleşmesi
- Hedef URL ve sorgulama meta verileri de dahil olmak üzere, test çalıştırıcısının yardımcı hizmete gönderdiği istek yükünü tanımlayın.
- Test katmanının temiz bir şekilde dallanabilmesi için belirteç, süre sonu ve hata nedeni ile yapılandırılmış bir yanıt döndürün.
- Daha hızlı önceliklendirme için simülatör günlüklerini ve yardımcı hizmet günlüklerini paylaşılan bir iz tanımlayıcıyla bağlantılı tutun.
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
evaluateJavaScript sıfır değerini döndürür |
Web Görünümü yüklemeyi tamamlamadı | JS'yi enjekte etmeden önce webView.isLoading == false'yi bekleyin |
| Simülatörden arka uca ulaşılamıyor | localhost'a erişilemiyor | 127.0.0.1'yi veya Mac'in ağ IP'sini kullanın; Uygulama Taşıma Güvenliğini kontrol edin |
| Jeton enjeksiyonu geri aramayı tetiklemiyor | Karmaşık nesnede iç içe geçmiş reCAPTCHA geri araması | ___grecaptcha_cfg.clients'nin tüm özelliklerini yinelemeli olarak yineleyin |
| XCUITest zaman aşımı çözümü beklerken | Uzun CaptchaAI çözüm süresi | CAPTCHA ile ilgili testler için test zaman aşımını 120+ saniyeye ayarlayın |
SSS
XCUITest, JavaScript'i doğrudan WKWebView'da çalıştırabilir mi?
Hayır. XCUITest, kullanıcı arayüzü öğeleriyle etkileşime girer ancak JavaScript'i değerlendiremez. Bu boşluğu kapatmak için uygulama kodunda bir test kancasına (yalnızca hata ayıklama derlemesi) ihtiyacınız vardır.
Bu yaklaşım CI/CD işlem hatlarında işe yarayacak mı?
Evet, çözücü arka ucunu CI makinesinde ve iOS Simulator'da çalıştırın. Çözücü hizmeti, her ortamda çalışan CaptchaAI ile HTTPS üzerinden iletişim kurar.
Test kancasının üretime gönderilmesini nasıl önleyebilirim?
Tüm test yardımcı kodunu #if DEBUG derleyici yönergelerine sarın. Kod, sürüm yapılarından çıkarılacaktır.
CAPTCHA üçüncü taraf bir SDK Web Görünümündeyse ne olur?
WebView'ı kontrol etmiyorsanız bunun yerine Appium'u kullanın; bu, uygulama tarafı kancalarına ihtiyaç duymadan herhangi bir WebView bağlamında execute_script yetenekleri sağlar.
İlgili Makaleler
- Api Kullanarak Recaptcha V2 Geri Arama Sorunu Nasıl Çözülür?
- Zapier Captchaai Kodsuz Otomasyon
- Recaptcha V2 Turnstilesi Aynı Sahada İşleme
Sonraki Adımlar
CaptchaAI'yi iOS test hattınıza entegre edin -API anahtarınızı alınve CAPTCHA korumalı akışlar aracılığıyla otomatikleştirin.
İlgili kılavuzlar: