Files
2nd/10_Wiki/Topics/Coding/Mobile_E2E_Testing.md
T
2026-05-09 21:08:02 +09:00

4.8 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
mobile-e2e-testing Mobile E2E Testing — Detox / Maestro / Appium Coding draft B conceptual 2026-05-09 2026-05-09
mobile
testing
detox
maestro
vibe-coding
language applicable_to
TS / YAML
iOS
Android
React Native
Detox
Maestro
Appium
EarlGrey
mobile UI test
instrumented test

Mobile E2E Testing

단위 테스트 충분 X — 실기 / 시뮬레이터 UI 흐름. Maestro = 가장 단순 (YAML), Detox = RN 친화 (gray-box), Appium = native 강력하지만 무거움.

📖 핵심 개념

  • E2E: 실제 앱 빌드 + 시뮬레이터 / 디바이스 + UI 조작.
  • Gray-box (Detox): JS 와 native 사이 연결 — RN 앱 idle 대기 자동.
  • Black-box (Appium / Maestro): UI 만 보고 조작.
  • Flake: UI 비동기 — wait / retry 가 핵심.

💻 코드 패턴

Maestro (YAML, 가장 단순)

# .maestro/login.yaml
appId: com.acme.app
---
- launchApp
- tapOn: "Email"
- inputText: "test@acme.com"
- tapOn: "Password"
- inputText: "password"
- tapOn: "Sign in"
- assertVisible: "Welcome"
- takeScreenshot: home
maestro test .maestro/
maestro studio  # 인터랙티브 record

Detox (RN 친화)

// e2e/login.test.js
describe('Login', () => {
  beforeAll(async () => { await device.launchApp(); });
  beforeEach(async () => { await device.reloadReactNative(); });

  it('should login', async () => {
    await element(by.id('email')).typeText('test@acme.com');
    await element(by.id('password')).typeText('password');
    await element(by.id('login')).tap();

    await waitFor(element(by.id('home')))
      .toBeVisible()
      .withTimeout(5000);
  });
});
detox build -c ios.sim.debug
detox test -c ios.sim.debug

Appium (native + WebDriver)

import { remote } from 'webdriverio';

const driver = await remote({
  port: 4723,
  capabilities: {
    platformName: 'iOS',
    'appium:platformVersion': '17.0',
    'appium:deviceName': 'iPhone 15',
    'appium:app': '/path/to/App.app',
    'appium:automationName': 'XCUITest',
  },
});

await driver.$('~email').setValue('test@acme.com');
await driver.$('~login').click();
const welcome = await driver.$('~Welcome');
await welcome.waitForDisplayed({ timeout: 5000 });

iOS native (XCUITest)

class LoginUITests: XCTestCase {
    func testLogin() {
        let app = XCUIApplication()
        app.launch()
        app.textFields["email"].tap()
        app.textFields["email"].typeText("a@b.com")
        app.secureTextFields["password"].typeText("pw")
        app.buttons["Sign in"].tap()
        XCTAssertTrue(app.staticTexts["Welcome"].waitForExistence(timeout: 5))
    }
}

Android native (Espresso)

@Test fun testLogin() {
    onView(withId(R.id.email)).perform(typeText("a@b.com"))
    onView(withId(R.id.password)).perform(typeText("pw"))
    onView(withId(R.id.login)).perform(click())
    onView(withId(R.id.welcome)).check(matches(isDisplayed()))
}

Flake 줄이기

// 명시적 wait — 임의 sleep 금지
await waitFor(element(by.id('home'))).toBeVisible().withTimeout(10000);

// 네트워크 mock (Detox)
await device.setURLBlacklist(['.*analytics.*']);

// 시간 / animation off
await device.setStatusBar({ time: '12:00' });

Visual regression

# Maestro
- assertVisible:
    text: "Welcome"
- takeScreenshot: home

# 비교 = 자체 도구 (Percy, Applitools, BackstopJS)

CI (GitHub Actions)

- name: Build iOS
  run: detox build -c ios.sim.release
- name: Test iOS
  run: detox test -c ios.sim.release --record-videos failing
- uses: actions/upload-artifact@v4
  if: failure()
  with: { path: artifacts/ }

Cloud test labs

  • BrowserStack / Sauce Labs / Firebase Test Lab — 다양 디바이스.
  • Maestro Cloud: Maestro 전용.

🤔 의사결정 기준

상황 추천
RN 앱 Detox 또는 Maestro
빠른 시작 / QA 친화 Maestro
강력 / WebDriver 친숙 Appium
iOS native only XCUITest
Android native only Espresso
다양 디바이스 BrowserStack / Sauce / Firebase
Visual regression Maestro + Percy / Applitools

안티패턴

  • Sleep 으로 wait: flake 폭발. waitFor.
  • Real network call: 외부 의존 + flake. mock / stub.
  • 모든 path E2E: 느림. critical path 만 (5-10).
  • Test ID 없음 (텍스트로만 식별): i18n 시 깨짐.
  • CI 만 — 로컬 안 됨: 디버그 어려움.
  • Snapshot 없는 실패: 원인 추측. video / screenshot.
  • Flaky 무시: 1% flake 가 100 test = 매번 깨짐.

🤖 LLM 활용 힌트

  • 새 RN 앱 = Maestro 시작.
  • testID 항상 부여 + waitFor.
  • CI artifact (video, screenshot) 필수.

🔗 관련 문서