गाइड

डेडिकेटेड Mac पर XCUITest से iOS UI टेस्ट ऑटोमेट करना

XCUITest ऑटोमेट करने के लिए xcodebuild test को सिम्युलेटर destination और -resultBundlePath के साथ चलाएँ, ऐसे Mac पर जिसमें यूज़र सेशन लॉग-इन हो। xcrun simctl से एक अलग सिम्युलेटर बनाएँ। टेस्ट को सिम्युलेटर क्लोन में बाँटने के लिए -parallel-testing-enabled YES जोड़ें, और अस्थिर टेस्ट दोबारा चलाने के लिए -retry-tests-on-failure। यहाँ का हर कमांड हमने Xcode 26.6 और iOS 26.5 सिम्युलेटर वाले Mac पर चलाया।

पहले क्या चाहिए

  • Xcode और कम से कम एक iOS सिम्युलेटर रनटाइम वाला Mac।
  • आपके प्रोजेक्ट में एक UI टेस्ट टारगेट, और उसे टेस्ट करने वाली shared scheme।
  • लॉग-इन किया हुआ डेस्कटॉप सेशन। UI टेस्ट Simulator ऐप चलाते हैं, इसलिए उन्हें चलाने वाली चीज़ उसी सेशन में होनी चाहिए। यानी CI एजेंट LaunchAgent के रूप में शुरू हो, LaunchDaemon के रूप में नहीं। Buildkite का plist टेम्पलेट भी यही कहता है: GUI मोड Xcode UI टेस्टिंग की अनुमति देता है, पर उसे लॉग-इन चाहिए।

1. Xcode और रनटाइम जाँचें

xcodebuild -version
sudo xcodebuild -runFirstLaunch
xcrun simctl list runtimes
xcrun simctl list devicetypes | grep iPhone

-runFirstLaunch पैकेज इंस्टॉल करता है और लाइसेंस स्वीकार करता है। हर Xcode इंस्टॉल या अपग्रेड के बाद इसे चलाएँ।

2. सिर्फ़ CI के लिए एक सिम्युलेटर बनाएँ

अलग डिवाइस CI को उन चीज़ों से दूर रखता है, जो किसी ने हाथ से खोली हों। bootstatus -b इसे बूट करता है और तैयार होने तक इंतज़ार करता है।

UDID=$(xcrun simctl create "CI iPhone 17" "iPhone 17" com.apple.CoreSimulator.SimRuntime.iOS-26-5)
xcrun simctl bootstatus "$UDID" -b
echo "$UDID"

destination में UDID इस्तेमाल करें: -destination "platform=iOS Simulator,id=$UDID"। नाम भी काम करता है, जैसे name=iPhone 17,OS=26.5, पर दो डिवाइस का नाम एक हो सकता है।

3. result bundle के साथ UI टेस्ट चलाएँ

rm -rf build/TestResults.xcresult
xcodebuild test \
  -project MyApp.xcodeproj \
  -scheme MyApp \
  -destination "platform=iOS Simulator,id=$UDID" \
  -derivedDataPath build/DerivedData \
  -resultBundlePath build/TestResults.xcresult

result bundle में हर टेस्ट का नतीजा, लॉग, स्क्रीनशॉट और फ़ेलियर होता है। कमांड लाइन पर सारांश पढ़ें:

xcrun xcresulttool get test-results summary --path build/TestResults.xcresult

यह हर डिवाइस के लिए passed, failed और skipped गिनती के साथ JSON प्रिंट करता है। bundle को ditto -c -k --keepParent से zip करके अपने CI रन में आर्टिफ़ैक्ट के रूप में जोड़ें।

4. एक बार बिल्ड करें, कई बार टेस्ट करें

बिल्ड को टेस्ट रन से अलग करें। तब आप बिना फिर कंपाइल किए कुछ टेस्ट दोबारा चला सकते हैं।

xcodebuild build-for-testing -project MyApp.xcodeproj -scheme MyApp \
  -destination "platform=iOS Simulator,id=$UDID" -derivedDataPath build/DerivedData

xcodebuild test-without-building -project MyApp.xcodeproj -scheme MyApp \
  -destination "platform=iOS Simulator,id=$UDID" -derivedDataPath build/DerivedData \
  -only-testing:MyAppUITests/LoginTests/testSignIn

-only-testing में Target, Target/Class, या Target/Class/method दिया जा सकता है। -skip-testing इसी तरह उलटा काम करता है।

5. टेस्ट पैरेलल चलाएँ

xcodebuild test -project MyApp.xcodeproj -scheme MyApp \
  -destination "platform=iOS Simulator,id=$UDID" \
  -parallel-testing-enabled YES \
  -parallel-testing-worker-count 2 \
  -resultBundlePath build/TestResults.xcresult

Xcode सिम्युलेटर को क्लोन करता है और टेस्ट क्लास को क्लोन में बाँट देता है। हमारे लॉग में टेस्ट iPhone 17 के Clone 1 पर चलते दिखे। -parallel-testing-enabled scheme की सेटिंग को ओवरराइड करता है। 16 GB Mac पर 2 workers से शुरू करें, और बढ़ाने से पहले मापें। हर क्लोन मेमोरी में एक पूरा सिम्युलेटर है। एक साथ कई डिवाइस टाइप पर टेस्ट करने के लिए कई destinations दें और -maximum-concurrent-test-simulator-destinations सेट करें।

6. अस्थिर टेस्ट दोबारा चलाएँ

xcodebuild test -project MyApp.xcodeproj -scheme MyApp \
  -destination "platform=iOS Simulator,id=$UDID" \
  -retry-tests-on-failure \
  -test-iterations 3 \
  -resultBundlePath build/TestResults.xcresult

xcodebuild -help कहता है कि फ़ेल टेस्ट iteration की गिनती तक दोबारा चलता है। -test-iterations के बिना अधिकतम 3 है। हमने इसे ऐसे टेस्ट से जाँचा, जो एक बार फ़ेल होकर फिर पास होता है। रन TEST SUCCEEDED पर खत्म हुआ। सारांश में 2 टेस्ट के लिए 3 टेस्ट रन दिखे।

यानी retry अस्थिर टेस्ट को हरा कर देता है, और गड़बड़ी छिप जाती है। हर रन पर result bundle पढ़ें, और नज़र रखें कि किन टेस्ट को retry चाहिए था। हर कोशिश को नए प्रोसेस में चलाने के लिए -test-repetition-relaunch-enabled YES जोड़ें। अस्थिर टेस्ट पकड़ने के लिए -run-tests-until-failure इस्तेमाल करें। इसे -retry-tests-on-failure के साथ नहीं जोड़ा जा सकता।

7. अटके टेस्ट पर सीमा लगाएँ

-test-timeouts-enabled YES \
-default-test-execution-time-allowance 120 \
-maximum-test-execution-time-allowance 300

ये फ़्लैग टेस्ट कमांड में जोड़ें। तब किसी element का हमेशा इंतज़ार करने वाला UI टेस्ट अपनी तय सीमा के बाद फ़ेल हो जाता है। वह CI टाइमआउट तक मशीन को नहीं रोकता।

8. रन के बीच सिम्युलेटर साफ़ रखें

xcrun simctl shutdown "$UDID"
xcrun simctl erase "$UDID"
xcrun simctl delete unavailable

erase सिम्युलेटर का कंटेंट और सेटिंग रीसेट करता है। delete unavailable उन डिवाइस को हटाता है, जिन्हें मौजूदा Xcode अब सपोर्ट नहीं करता। हर Xcode अपग्रेड के बाद इसे चलाएँ।

यह रीबूट के बाद कैसे चलता रहता है

आपके बनाए सिम्युलेटर रीबूट के बाद भी बने रहते हैं। टेस्ट चलाने वाले CI एजेंट को वापस आना होता है। इसे LaunchAgent के रूप में चलाएँ, ऑटोमैटिक लॉग-इन चालू करें, और हर जॉब की शुरुआत में CI सिम्युलेटर को bootstatus -b से बूट करें। पहले से बूट डिवाइस पर भी यह कमांड सुरक्षित है।

हमें मिले एरर और उनके हल

  • Unable to find a device matching the provided destination specifier। वह नाम या OS मौजूद नहीं है। xcrun simctl list devices और रनटाइम की लिस्ट जाँचें।
  • xcodebuild: error: Existing file at -resultBundlePath। हर रन से पहले पुराना bundle मिटाएँ।
  • Unable to erase contents and settings in current state: Booted। erase करने से पहले सिम्युलेटर बंद करें।

डेडिकेटेड Mac क्यों मदद करता है

UI टेस्ट पहले टैप से पहले बहुत समय लगाते हैं। वे बिल्ड, सिम्युलेटर बूट और ऐप इंस्टॉल का इंतज़ार करते हैं। चालू रहने वाली मशीन DerivedData और बूट हुआ सिम्युलेटर तैयार रखती है। हमारे बेंचमार्क में हमने Xcode 26.6 से Wikipedia iOS ऐप बिल्ड किया। छोटे बदलाव के बाद जॉब को वॉर्म M6 पर 27 सेकंड लगे। नए GitHub-होस्टेड macos-26 रनर पर 269 सेकंड लगे। हमारे M6 में 12 CPU कोर हैं, इसलिए दो पैरेलल क्लोन के बाद भी बिल्ड के लिए जगह बचती है।

कब होस्टेड CI काफ़ी है

दिन में कुछ पुल रिक्वेस्ट पर चलने वाला छोटा टेस्ट सूट मीटर वाले मिनटों में ठीक बैठता है। GitHub की $0.062 प्रति macOS मिनट की दर पर (सितंबर 2026 में जाँची), $139 वाला Mac महीने में लगभग 2,242 मिनट के बाद फ़ायदे में आता है। उससे नीचे होस्टेड पर रहें। अगर आपको कई असली iPhone पर टेस्ट करना ही है, तो आपको device cloud चाहिए। Mac mini सिम्युलेटर चलाता है। क्या आपके पैरेलल सूट को 16 GB से ज़्यादा मेमोरी चाहिए? हमारे M5 Pro टियर में 48 या 64 GB है, पर वे प्री-ऑर्डर पर हैं, इसलिए लगभग एक हफ़्ते के इंतज़ार की योजना बनाएँ।

iOS CI/CD पाइपलाइन गाइड दिखाती है कि पूरी पाइपलाइन में UI टेस्ट कहाँ आते हैं। कैलकुलेटर आपके अपने आंकड़ों से हिसाब लगाता है।

अक्सर पूछे जाने वाले सवाल

क्या xcodebuild में फ़ेल टेस्ट दोबारा चलाने का फ़्लैग है?

+

हाँ। -retry-tests-on-failure फ़ेल टेस्ट को -test-iterations बार तक, या डिफ़ॉल्ट रूप से 3 बार तक, दोबारा चलाता है। इसे -run-tests-until-failure के साथ नहीं जोड़ा जा सकता।

कमांड लाइन से XCUITest टेस्ट पैरेलल कैसे चलाऊँ?

+

-parallel-testing-enabled YES जोड़ें, और चाहें तो -parallel-testing-worker-count भी। Xcode सिम्युलेटर को क्लोन करता है और टेस्ट क्लास को क्लोन में बाँट देता है।

क्या XCUITest headless Mac पर चल सकता है?

+

इसे लॉग-इन किया हुआ यूज़र सेशन चाहिए, क्योंकि UI टेस्ट Simulator ऐप चलाते हैं। CI एजेंट को LaunchAgent के रूप में चलाएँ और ऑटोमैटिक लॉग-इन चालू करें।

Xcode खोले बिना xcodebuild के टेस्ट नतीजे कैसे पढ़ूँ?

+

-resultBundlePath दें, फिर bundle पर xcrun xcresulttool get test-results summary --path चलाएँ। यह गिनती JSON में प्रिंट करता है।

संबंधित गाइड