Langsung ke konten

Pengujian UI Otomatis pada Server Mac: XCTest, Appium & Selenium

Siapkan server Mac khusus untuk pengujian UI otomatis 24/7. Jalankan pengujian XCTest, XCUITest, Appium, dan Selenium secara paralel di beberapa iOS Simulator dengan hasil yang konsisten dan dapat direproduksi.

Pengujian Baca 30 menit Diperbarui Oktober 2026

Mengapa Hardware Khusus untuk Pengujian?

Pengujian UI pada mesin bersama atau lokal menimbulkan ketidakstabilan, inkonsistensi, dan bottleneck. Server build Mac mini M4 khusus mengatasi masalah-masalah ini.

Hasil yang Konsisten

Tidak ada proses lain yang bersaing memperebutkan CPU atau RAM. Pengujian berjalan dalam lingkungan yang bersih dan terkontrol setiap saat, menghilangkan kegagalan pengujian yang tidak stabil akibat perebutan sumber daya.

Eksekusi Paralel

Jalankan pengujian di beberapa instance iOS Simulator secara bersamaan. Mac mini M4 Pro (CPU 12-core, 24GB) menangani 4-6 instance Simulator paralel tanpa penurunan performa.

Ketersediaan 24/7

Pengujian berjalan kapan saja: suite regresi malam hari, validasi pasca-merge, atau sesuai permintaan. Server selalu siap, tanpa memerlukan laptop developer.

XCTest pada Mac Khusus

XCTest adalah framework pengujian bawaan Apple, yang disertakan dengan Xcode. XCUITest memperluasnya untuk pengujian UI. Keduanya berjalan secara native dan tidak memerlukan setup tambahan selain Xcode.

Menjalankan XCTest dari Command Line

# Use any device listed by: xcrun simctl list devices available
# Run all unit tests
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
  -resultBundlePath ./TestResults/UnitTests.xcresult

# Run only UI tests
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyAppUITests \
  -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
  -resultBundlePath ./TestResults/UITests.xcresult

# Run a specific test class
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -only-testing:MyAppTests/LoginTests

# Run a specific test method
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -only-testing:MyAppTests/LoginTests/testSuccessfulLogin

Pengujian pada Beberapa Destinasi

# Test on multiple iPhone models simultaneously
# (each name must exist in: xcrun simctl list devices available;
#  create a missing one with: xcrun simctl create "iPhone 16e" "iPhone 16e")
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro Max' \
  -destination 'platform=iOS Simulator,name=iPhone 16e' \
  -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M4)' \
  -resultBundlePath ./TestResults/MultiDevice.xcresult

# List all available destinations
xcodebuild -showdestinations \
  -workspace MyApp.xcworkspace \
  -scheme MyApp

Result Bundle dan Screenshot

# Test results summary as JSON (Xcode 16 and later)
xcrun xcresulttool get test-results summary \
  --path ./TestResults/UITests.xcresult

# Every test with its result
xcrun xcresulttool get test-results tests \
  --path ./TestResults/UITests.xcresult

# Export test attachments (screenshots, videos)
xcrun xcresulttool export attachments \
  --path ./TestResults/UITests.xcresult \
  --output-path ./TestArtifacts

Setup Appium untuk iOS

Appium adalah framework otomasi open-source yang memungkinkan Anda menulis pengujian dalam bahasa apa pun (Python, JavaScript, Java, dll.) dan menjalankannya terhadap aplikasi iOS pada simulator atau perangkat. Ia menggunakan driver XCUITest milik Apple di baliknya.

Instal Appium pada Server Mac Anda

# Install Node.js via Homebrew
brew install node

# Install Appium globally (npm installs the latest release)
npm install -g appium

# Install the XCUITest driver for iOS
appium driver install xcuitest

# Verify installation
appium --version
appium driver list --installed

# Check the driver's requirements (Xcode, WebDriverAgent, etc.)
appium driver doctor xcuitest

# Start Appium server
appium server --address 127.0.0.1 --port 4723

Mengonfigurasi Desired Capabilities

# Example capabilities (JSON format, Appium 2 and later)
# The Simulator is picked by name; add "appium:platformVersion"
# to pin a specific iOS version
{
    "platformName": "iOS",
    "appium:automationName": "XCUITest",
    "appium:deviceName": "iPhone 17",
    "appium:app": "/path/to/MyApp.app",
    "appium:noReset": false,
    "appium:wdaStartupRetries": 3,
    "appium:wdaStartupRetryInterval": 20000,
    "appium:simulatorStartupTimeout": 120000
}

Contoh Pengujian Appium (Python)

# Install Appium Python client
# pip install Appium-Python-Client

from appium import webdriver
from appium.options.ios import XCUITestOptions
from appium.webdriver.common.appiumby import AppiumBy

# Configure options
options = XCUITestOptions()
options.platform_name = "iOS"
options.device_name = "iPhone 17"
# Optional: options.platform_version pins a specific iOS version
options.app = "/path/to/MyApp.app"

# Connect to Appium server
driver = webdriver.Remote(
    command_executor="http://127.0.0.1:4723",
    options=options
)

try:
    # Wait for app to load
    driver.implicitly_wait(10)

    # Find and tap login button
    login_button = driver.find_element(
        AppiumBy.ACCESSIBILITY_ID, "loginButton"
    )
    login_button.click()

    # Enter username
    username_field = driver.find_element(
        AppiumBy.ACCESSIBILITY_ID, "usernameField"
    )
    username_field.send_keys("testuser@example.com")

    # Enter password
    password_field = driver.find_element(
        AppiumBy.ACCESSIBILITY_ID, "passwordField"
    )
    password_field.send_keys("password123")

    # Submit login
    submit_button = driver.find_element(
        AppiumBy.ACCESSIBILITY_ID, "submitButton"
    )
    submit_button.click()

    # Verify welcome screen
    welcome_label = driver.find_element(
        AppiumBy.ACCESSIBILITY_ID, "welcomeLabel"
    )
    assert "Welcome" in welcome_label.text

    print("Test PASSED: Login successful")

finally:
    driver.quit()

Contoh Pengujian Appium (JavaScript)

// npm install webdriverio @wdio/cli

const { remote } = require('webdriverio');

async function runTest() {
    const driver = await remote({
        protocol: 'http',
        hostname: '127.0.0.1',
        port: 4723,
        path: '/',
        capabilities: {
            platformName: 'iOS',
            'appium:automationName': 'XCUITest',
            'appium:deviceName': 'iPhone 17',
            'appium:app': '/path/to/MyApp.app'
        }
    });

    try {
        // Tap login button
        const loginBtn = await driver.$('~loginButton');
        await loginBtn.click();

        // Enter credentials
        const username = await driver.$('~usernameField');
        await username.setValue('testuser@example.com');

        const password = await driver.$('~passwordField');
        await password.setValue('password123');

        // Submit
        const submit = await driver.$('~submitButton');
        await submit.click();

        // Verify
        const welcome = await driver.$('~welcomeLabel');
        const text = await welcome.getText();
        console.assert(text.includes('Welcome'), 'Login test failed');

        console.log('Test PASSED: Login successful');
    } finally {
        await driver.deleteSession();
    }
}

runTest();

Selenium untuk Safari

macOS menyertakan Safari dan safaridriver secara bawaan. Tidak perlu mengunduh driver browser tambahan, tidak seperti Chrome atau Firefox pada platform lain.

Mengaktifkan safaridriver

# Enable the Safari WebDriver (one-time setup)
safaridriver --enable

# Verify safaridriver is working
safaridriver --version

# For headless-like automation, enable "Allow Remote Automation"
# in Safari > Settings > Advanced > Show Develop menu
# Then: Develop > Allow Remote Automation
# Or via command line:
defaults write com.apple.Safari AllowRemoteAutomation 1

Pengujian Safari Selenium (Python)

# pip install selenium

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

# Create Safari driver
driver = webdriver.Safari()

try:
    # Navigate to your web app
    driver.get("https://your-webapp.com")

    # Wait for page to load
    wait = WebDriverWait(driver, 10)

    # Find and click a button
    login_link = wait.until(
        EC.element_to_be_clickable((By.CSS_SELECTOR, "a.login-btn"))
    )
    login_link.click()

    # Fill in form
    email_input = wait.until(
        EC.presence_of_element_located((By.ID, "email"))
    )
    email_input.send_keys("test@example.com")

    password_input = driver.find_element(By.ID, "password")
    password_input.send_keys("testpassword")

    # Submit form
    submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
    submit_btn.click()

    # Verify redirect to dashboard
    wait.until(EC.url_contains("/dashboard"))
    assert "/dashboard" in driver.current_url

    print("Test PASSED: Safari login flow works correctly")

finally:
    driver.quit()

Pengujian Safari Selenium (JavaScript)

// npm install selenium-webdriver

const { Builder, By, until } = require('selenium-webdriver');

async function safariTest() {
    const driver = await new Builder()
        .forBrowser('safari')
        .build();

    try {
        await driver.get('https://your-webapp.com');

        // Click login
        const loginBtn = await driver.findElement(By.css('a.login-btn'));
        await loginBtn.click();

        // Fill form
        const email = await driver.findElement(By.id('email'));
        await email.sendKeys('test@example.com');

        const password = await driver.findElement(By.id('password'));
        await password.sendKeys('testpassword');

        // Submit
        const submit = await driver.findElement(
            By.css("button[type='submit']")
        );
        await submit.click();

        // Verify
        await driver.wait(until.urlContains('/dashboard'), 10000);
        const url = await driver.getCurrentUrl();
        console.assert(url.includes('/dashboard'));

        console.log('Test PASSED: Safari login flow works');
    } finally {
        await driver.quit();
    }
}

safariTest();

Eksekusi Pengujian Paralel

Menjalankan pengujian secara paralel secara dramatis mengurangi total waktu eksekusi suite pengujian Anda. Mac Mini M4 Pro dapat dengan nyaman menjalankan 4-6 instance Simulator secara bersamaan.

Pengujian Paralel XCTest

# Enable parallel testing with xcodebuild
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -parallel-testing-enabled YES \
  -parallel-testing-worker-count 4 \
  -resultBundlePath ./TestResults/Parallel.xcresult

# Parallel testing across multiple device types
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -destination 'platform=iOS Simulator,name=iPhone 16e' \
  -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M4)' \
  -parallel-testing-enabled YES \
  -resultBundlePath ./TestResults/MultiDeviceParallel.xcresult

Mengelola Instance Simulator

# List all available simulators
xcrun simctl list devices available

# Create custom simulator instances for testing
# (without a runtime argument, simctl uses the newest iOS runtime)
xcrun simctl create "Test-iPhone-1" "iPhone 17"
xcrun simctl create "Test-iPhone-2" "iPhone 17"
xcrun simctl create "Test-iPhone-3" "iPhone 17"
xcrun simctl create "Test-iPhone-4" "iPhone 17"

# Boot multiple simulators
xcrun simctl boot "Test-iPhone-1"
xcrun simctl boot "Test-iPhone-2"
xcrun simctl boot "Test-iPhone-3"
xcrun simctl boot "Test-iPhone-4"

# Check booted simulators
xcrun simctl list devices booted

# Shut down all simulators
xcrun simctl shutdown all

# Delete all test simulators
xcrun simctl delete "Test-iPhone-1"
xcrun simctl delete "Test-iPhone-2"
xcrun simctl delete "Test-iPhone-3"
xcrun simctl delete "Test-iPhone-4"

Performa Pengujian Paralel

Performa Pengujian Paralel
Durasi 200 Pengujian UI Peningkatan Kecepatan
1 Simulator (berurutan) 45 menit Acuan
2 Simulator (paralel) 24 menit 1,9x lebih cepat
4 Simulator (paralel) 13 menit 3,5x lebih cepat
6 Simulator (paralel) 10 menit 4,5x lebih cepat

Catatan: Hasil diukur pada Mac Mini M4 Pro (14-core, RAM 24GB). Jumlah Simulator paralel yang optimal bergantung pada kompleksitas pengujian dan kebutuhan memori Anda. Untuk sebagian besar suite pengujian UI, 4 worker paralel memberikan keseimbangan terbaik antara kecepatan dan stabilitas.

Integrasi CI/CD

Integrasikan pengujian otomatis Anda ke dalam pipeline CI/CD untuk eksekusi pengujian otomatis pada setiap push, pull request, atau interval terjadwal.

Workflow GitHub Actions untuk Pengujian Otomatis

# .github/workflows/ios-tests.yml
name: iOS Automated Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # Nightly regression tests at 2 AM UTC
    - cron: '0 2 * * *'

env:
  # Xcode for every job, no sudo needed (path from: xcodes installed)
  DEVELOPER_DIR: /Applications/Xcode-27.0.0.app/Contents/Developer

jobs:
  unit-tests:
    name: Unit Tests
    runs-on: self-hosted
    timeout-minutes: 30

    steps:
      - uses: actions/checkout@v4

      - name: Resolve dependencies
        run: |
          xcodebuild -resolvePackageDependencies \
            -workspace MyApp.xcworkspace \
            -scheme MyApp

      - name: Run unit tests
        run: |
          xcodebuild test \
            -workspace MyApp.xcworkspace \
            -scheme MyApp \
            -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
            -parallel-testing-enabled YES \
            -resultBundlePath ${{ runner.temp }}/UnitTests.xcresult

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: unit-test-results
          path: ${{ runner.temp }}/UnitTests.xcresult

  ui-tests:
    name: UI Tests
    runs-on: self-hosted
    timeout-minutes: 60
    needs: unit-tests

    steps:
      - uses: actions/checkout@v4

      - name: Boot simulators for parallel testing
        run: |
          xcrun simctl boot "iPhone 17" || true
          xcrun simctl boot "iPhone 16e" || true

      - name: Run UI tests in parallel
        run: |
          xcodebuild test \
            -workspace MyApp.xcworkspace \
            -scheme MyAppUITests \
            -destination 'platform=iOS Simulator,name=iPhone 17' \
            -destination 'platform=iOS Simulator,name=iPhone 16e' \
            -parallel-testing-enabled YES \
            -parallel-testing-worker-count 4 \
            -resultBundlePath ${{ runner.temp }}/UITests.xcresult

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: ui-test-results
          path: ${{ runner.temp }}/UITests.xcresult

      - name: Cleanup simulators
        if: always()
        run: xcrun simctl shutdown all

Menjalankan Pengujian Appium di CI

# Add to your GitHub Actions workflow
      - name: Start Appium server
        run: |
          appium server --address 127.0.0.1 --port 4723 &
          sleep 5
          curl http://127.0.0.1:4723/status

      - name: Build app for testing
        run: |
          xcodebuild build-for-testing \
            -workspace MyApp.xcworkspace \
            -scheme MyApp \
            -destination 'generic/platform=iOS Simulator'

      - name: Run Appium tests
        run: |
          cd tests/appium
          pip install -r requirements.txt
          pytest test_login.py test_checkout.py \
            --junitxml=results.xml -v

      - name: Stop Appium server
        if: always()
        run: pkill -f appium || true

Pelaporan Pengujian

Hasilkan dan proses laporan pengujian untuk melacak tren kualitas dan dengan cepat mengidentifikasi pengujian yang gagal.

Bekerja dengan Bundle xcresult

# Test results summary as JSON (Xcode 16 and later)
xcrun xcresulttool get test-results summary \
  --path ./TestResults.xcresult

# Results of every test, then the details of one of them
# (TEST_ID: an identifier printed by the tests command)
xcrun xcresulttool get test-results tests \
  --path ./TestResults.xcresult
xcrun xcresulttool get test-results test-details \
  --test-id "TEST_ID" \
  --path ./TestResults.xcresult

# Export all attachments (screenshots, logs)
xcrun xcresulttool export attachments \
  --path ./TestResults.xcresult \
  --output-path ./artifacts

# Merge multiple xcresult bundles
xcrun xcresulttool merge \
  ./UnitTests.xcresult \
  ./UITests.xcresult \
  --output-path ./MergedResults.xcresult

Mengonversi ke JUnit XML

JUnit XML adalah format universal yang didukung oleh platform CI/CD, integrasi Slack, dan dashboard pengujian.

# Option 1: write JUnit XML while the tests run (xcbeautify)
brew install xcbeautify
set -o pipefail && xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -resultBundlePath ./TestResults/Tests.xcresult \
  | xcbeautify --report junit --report-path ./reports
# Writes ./reports/junit.xml

# Option 2: convert existing .xcresult bundles with fastlane's
# trainer action (path: the folder that holds them)
fastlane run trainer path:"./TestResults" output_directory:"./reports"

# The generated JUnit XML works with:
# - GitHub Actions test summaries
# - Jenkins Test Result plugin
# - GitLab CI test reporting
# - Slack notifications via CI integrations

Screenshot saat Kegagalan

// In your XCUITest, add screenshot capture on failure:
// XCTestCase+Screenshots.swift

import XCTest

extension XCTestCase {
    override func tearDown() {
        if testRun?.hasSucceeded == false {
            let screenshot = XCUIScreen.main.screenshot()
            let attachment = XCTAttachment(screenshot: screenshot)
            attachment.name = "Failure-\(name)"
            attachment.lifetime = .keepAlways
            add(attachment)
        }
        super.tearDown()
    }
}

Praktik Terbaik

Isolasi Pengujian

Setiap pengujian harus independen dan tidak bergantung pada state dari pengujian sebelumnya. Reset state aplikasi sebelum setiap pengujian.

// In your XCUITest setUp() method:
override func setUp() {
    super.setUp()
    continueAfterFailure = false

    let app = XCUIApplication()
    app.launchArguments = ["--uitesting", "--reset-state"]
    app.launchEnvironment = [
        "DISABLE_ANIMATIONS": "1",
        "UI_TEST_MODE": "true"
    ]
    app.launch()
}

Manajemen Simulator

Bersihkan simulator di antara proses pengujian untuk mencegah kebocoran state dan masalah ruang disk.

#!/bin/bash
# cleanup-simulators.sh - Run before and after test suites

# Shutdown all running simulators
xcrun simctl shutdown all

# Erase all simulator content and settings
xcrun simctl erase all

# Delete unavailable simulators
xcrun simctl delete unavailable

# Clear DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/*

# Clear simulator logs
rm -rf ~/Library/Logs/CoreSimulator/*

echo "Simulator cleanup complete"

Nonaktifkan Animasi untuk Kecepatan

Nonaktifkan animasi dalam aplikasi Anda selama pengujian UI untuk mengurangi waktu eksekusi pengujian dan ketidakstabilan.

// In your AppDelegate or App struct:
#if DEBUG
if CommandLine.arguments.contains("--uitesting") {
    UIView.setAnimationsEnabled(false)
}
#endif

// Also disable Simulator animations via command line:
// Set Simulator > Debug > Slow Animations = OFF
defaults write com.apple.iphonesimulator SlowMotionAnimation -bool NO

Coba Ulang Pengujian yang Tidak Stabil

Gunakan mekanisme coba ulang pengujian bawaan Xcode untuk menangani pengujian UI yang sesekali tidak stabil.

# Run each failing test up to 3 times in total
xcodebuild test \
  -workspace MyApp.xcworkspace \
  -scheme MyAppUITests \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -retry-tests-on-failure \
  -test-iterations 3 \
  -resultBundlePath ./TestResults.xcresult

Pantau Ruang Disk

Simulator dan artefak pengujian mengonsumsi ruang disk yang signifikan. Siapkan pembersihan otomatis.

# Add to crontab for daily cleanup at midnight
# crontab -e
0 0 * * * /usr/local/bin/cleanup-test-artifacts.sh

# cleanup-test-artifacts.sh
#!/bin/bash
# Remove test results older than 7 days
find ~/TestResults -name "*.xcresult" -mtime +7 -delete

# Remove DerivedData older than 3 days
find ~/Library/Developer/Xcode/DerivedData \
  -maxdepth 1 -mtime +3 -exec rm -rf {} +

# Remove old simulator logs
find ~/Library/Logs/CoreSimulator -mtime +3 -delete

# Check remaining disk space
df -h / | tail -1

Pertanyaan yang Sering Diajukan

Berapa banyak simulator paralel yang dapat ditangani Mac Mini M4 Pro?
Dengan CPU 12-core dan memori 24GB, Mac mini M4 Pro dengan nyaman menangani 4-6 instance iOS Simulator paralel untuk pengujian UI. Untuk pengujian unit saja (tanpa GUI Simulator), Anda dapat menjalankan lebih banyak worker paralel. Kami merekomendasikan memulai dengan 4 dan menambahnya berdasarkan kebutuhan memori suite pengujian Anda.
Bisakah saya menjalankan pengujian secara headless tanpa VNC?
Ya. Pengujian XCTest dan Appium berjalan sepenuhnya dari command line via SSH. iOS Simulator berjalan dalam mode "headless" ketika diluncurkan via xcodebuild tanpa sesi tampilan. Anda tidak perlu VNC terhubung selama eksekusi pengujian. VNC hanya berguna untuk men-debug pengujian yang gagal secara visual.
Bagaimana cara menangani pengujian UI yang tidak stabil?
Pertama, pastikan isolasi pengujian (reset state aplikasi sebelum setiap pengujian). Nonaktifkan animasi. Gunakan explicit wait alih-alih sleep(). Gunakan flag -retry-tests-on-failure milik Xcode untuk secara otomatis mencoba ulang pengujian yang gagal. Pada server macOS khusus, ketidakstabilan akibat perebutan sumber daya dihilangkan, yang merupakan penyebab paling umum pengujian tidak stabil pada mesin CI bersama.
Bisakah saya menguji pada perangkat iOS asli dari Mac jarak jauh?
Ya. Teruskan iPhone atau iPad fisik dari komputer Anda ke Mac dengan VirtualHere, dan perangkat tersebut akan muncul sebagai perangkat yang terhubung bagi Xcode, xcodebuild, dan Appium, sehingga Anda dapat menjalankan pengujian di sana. Untuk menginstal build bagi pengujian manual, gunakan TestFlight. Untuk matriks perangkat yang besar, jalankan suite XCUITest atau Appium Anda di cloud perangkat asli seperti BrowserStack, AWS Device Farm, atau Sauce Labs. Perlu dicatat bahwa Xcode Cloud milik Apple hanya menjalankan pengujian di simulator.
Bagaimana dengan pengujian pada versi iOS yang berbeda?
Anda dapat menginstal beberapa runtime iOS Simulator pada Mac yang sama. Gunakan xcodebuild -downloadPlatform iOS untuk yang terbaru, atau unduh runtime yang lebih lama dari Xcode > Settings > Components. Kemudian tentukan versi OS dalam destinasi pengujian Anda: -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.0'.

Jalankan Suite Pengujian Anda 24/7

Dapatkan Mac Mini M4 Pro khusus untuk pengujian otomatis. Eksekusi paralel, hasil konsisten, dan ketersediaan selalu aktif.

Harga My Remote Mac dalam USD, belum termasuk pajak.

Butuh detail lebih lanjut?

Jelajahi dokumentasi lengkap untuk panduan langkah demi langkah, referensi konfigurasi, dan pemecahan masalah.

Buka dokumentasi →