End-to-End Testing di Mobile
E2E testing di mobile lebih kompleks dari web karena perlu berinteraksi dengan native UI, permissions, gestures, dan platform-specific behavior. Detox dari Wix adalah framework E2E testing paling populer untuk React Native.
Kenapa E2E Testing di Mobile?
- User flows kritis — Login, checkout, onboarding harus selalu bekerja
- Regresi visual — Update library bisa break layout tanpa error
- Platform differences — Bug yang muncul hanya di iOS atau hanya di Android
- Gesture interactions — Swipe, pinch, long-press yang sulit di-unit-test
Setup Detox
# Install
npm install detox --save-dev
npm install jest-circus --save-dev
# iOS: install applesimutils
brew tap wix/brew
brew install applesimutils
# Init Detox config
npx detox init
// .detoxrc.js
module.exports = {
testRunner: {
args: { $0: 'jest', config: 'e2e/jest.config.js' },
jest: { setupTimeout: 120000 },
},
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/MyApp.app',
build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build: 'cd android && ./gradlew assembleDebug',
},
},
devices: {
simulator: { type: 'ios.simulator', device: { type: 'iPhone 15' } },
emulator: { type: 'android.emulator', device: { avdName: 'Pixel_7' } },
},
configurations: {
'ios.sim.debug': { device: 'simulator', app: 'ios.debug' },
'android.emu.debug': { device: 'emulator', app: 'android.debug' },
},
};
Menulis Test
// e2e/login.test.js
describe('Login Flow', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('should login with valid credentials', async () => {
// Ketik email
await element(by.id('email-input')).typeText('[email protected]');
// Ketik password
await element(by.id('password-input')).typeText('password123');
// Tap login button
await element(by.id('login-button')).tap();
// Verify navigasi ke home
await expect(element(by.id('home-screen'))).toBeVisible();
});
it('should show error for invalid credentials', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('wrong');
await element(by.id('login-button')).tap();
await expect(element(by.text('Invalid credentials'))).toBeVisible();
});
});
Test Matchers & Actions
// Matchers
element(by.id('testID')); // testID prop
element(by.text('Hello')); // Exact text
element(by.label('Submit')); // Accessibility label
// Actions
await element(by.id('input')).typeText('hello');
await element(by.id('button')).tap();
await element(by.id('scroll')).scroll(200, 'down');
await element(by.id('item')).swipe('left', 'fast');
await element(by.id('input')).clearText();
// Assertions
await expect(element(by.id('title'))).toBeVisible();
await expect(element(by.id('error'))).not.toExist();
await expect(element(by.id('input'))).toHaveText('expected');
Running Tests
# Build app for testing
npx detox build --configuration ios.sim.debug
# Run tests
npx detox test --configuration ios.sim.debug
# Run specific test file
npx detox test --configuration ios.sim.debug e2e/login.test.js
Tips
- Tambahkan
testID— Jangan rely pada text matcher, testID lebih reliable - Mock API — Gunakan mock server (MSW) agar test deterministic
- CI integration — Detox bisa jalan di CI (GitHub Actions + macOS runner untuk iOS)