Giới thiệu
Trong môi trường phát triển ứng dụng di động, việc đảm bảo tính ổn định và trải nghiệm người dùng nhất quán trên cả iOS và Android là một thách thức lớn. Các công cụ kiểm thử đơn vị (unit test) và kiểm thử thành phần (component test) chỉ bao phủ một phần nhỏ của luồng người dùng thực tế. Đó là lý do kiểm thử end-to-end (E2E) trở thành một phần không thể thiếu trong quy trình phát triển hiện đại.
Detox là một framework E2E được thiết kế đặc biệt cho React Native, cho phép chạy các kịch bản tự động trên thiết bị thực hoặc trình giả lập, đồng thời tích hợp chặt chẽ với CI/CD. Bài viết này sẽ đi sâu vào cách thiết lập môi trường, viết test mẫu, tối ưu hoá tốc độ chạy test và tích hợp Detox vào pipeline CI/CD.
Tại sao cần kiểm thử E2E cho React Native?
React Native cho phép chia sẻ phần lớn code logic giữa iOS và Android, nhưng các thành phần gốc (native modules) và hành vi hệ điều hành vẫn có thể khác nhau. Kiểm thử E2E giúp phát hiện các lỗi:
- Giao diện không hiển thị đúng trên một nền tảng.
- Quy trình điều hướng (navigation) bị gián đoạn.
- Thao tác với các API gốc như Camera, GPS, hoặc Push Notification.
- Hiệu suất chậm khi thực hiện các thao tác phức tạp.
Nhờ việc mô phỏng hành vi người dùng thực tế, Detox cung cấp phản hồi nhanh và đáng tin cậy, giảm thiểu rủi ro khi đưa ứng dụng lên store.
Chuẩn bị môi trường
Yêu cầu hệ thống
Để chạy Detox, bạn cần:
- Node.js (phiên bản LTS, ví dụ 18.x).
- Yarn hoặc npm.
- Xcode (đối với iOS) và Android SDK (đối với Android).
- Máy ảo hoặc thiết bị thực để chạy test.
Cài đặt Detox CLI
npm install -g detox-cli
Detox CLI cung cấp các lệnh để khởi tạo, biên dịch và chạy test. Đối với dự án mới, bạn có thể sử dụng lệnh detox init để tạo cấu hình mặc định.
Cài đặt phụ thuộc trong dự án React Native
Thêm Detox và các thư viện hỗ trợ vào devDependencies:
npm install --save-dev detox npm install --save-dev jest npm install --save-dev @testing-library/react-native
Detox yêu cầu một bộ test runner, thường là Jest. Đảm bảo file jest.config.js có cấu hình sau:
module.exports = {
preset: 'react-native',
testRunner: 'jest-circus/runner',
testTimeout: 120000,
reporters: ['detox/runners/jest/streamlineReporter'],
setupFilesAfterEnv: ['/e2e/init.js'],
};Thiết lập cấu hình Detox
Detox sử dụng file detox.config.js (hoặc .detoxrc.json) để mô tả các thiết bị và build target. Dưới đây là một cấu hình mẫu hỗ trợ cả iOS và Android:
module.exports = {
testRunner: 'jest',
runnerConfig: 'e2e/config.json',
apps: {
"ios.debug": {
type: "ios.app",
binaryPath: "ios/build/Build/Products/Debug-iphonesimulator/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 assembleAndroidTest -DtestBuildType=debug && cd .."
}
},
devices: {
"simulator": {
type: "ios.simulator",
name: "iPhone 14"
},
"emulator": {
type: "android.emulator",
name: "Pixel_5_API_33"
}
},
configurations: {
"ios.sim.debug": {
device: "simulator",
app: "ios.debug"
},
"android.emu.debug": {
device: "emulator",
app: "android.debug"
}
}
};Lưu ý:
- Đường dẫn
binaryPathphải trỏ tới file .app (iOS) hoặc .apk (Android) đã được biên dịch. - Command
buildsẽ được gọi mỗi khi chạydetox build. - Đặt tên thiết bị sao cho phù hợp với trình giả lập đã cài đặt.
Viết test E2E đầu tiên
Tạo file khởi tạo
File e2e/init.js sẽ khởi tạo Detox trước mỗi test suite:
const detox = require('detox');
const config = require('../detox.config.js');
beforeAll(async () => {
await detox.init(config);
});
afterAll(async () => {
await detox.cleanup();
});Test mẫu: Đăng nhập
Giả sử ứng dụng có màn hình đăng nhập với các trường emailInput, passwordInput và nút loginButton. Detox sử dụng các matcher dựa trên testID được đặt trong component React Native.
describe('Đăng nhập người dùng', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it('đăng nhập thành công với thông tin hợp lệ', async () => {
await element(by.id('emailInput')).typeText('[email protected]');
await element(by.id('passwordInput')).typeText('Password123');
await element(by.id('loginButton')).tap();
// Kiểm tra chuyển sang màn hình chính
await expect(element(by.id('homeScreen'))).toBeVisible();
});
it('hiển thị lỗi khi email không hợp lệ', async () => {
await element(by.id('emailInput')).typeText('invalid-email');
await element(by.id('passwordInput')).typeText('Password123');
await element(by.id('loginButton')).tap();
await expect(element(by.text('Email không hợp lệ'))).toBeVisible();
});
});Trong thực tế, bạn nên đặt testID cho mọi thành phần quan trọng để Detox có thể truy cập nhanh.
Tối ưu hoá tốc độ chạy test
Detox có thể chậm nếu mỗi test khởi động lại toàn bộ ứng dụng. Các kỹ thuật sau giúp giảm thời gian:
- Reuse device: Sử dụng tùy chọn
--reusekhi chạydetox testđể không khởi động lại trình giả lập. - Parallel execution: Chạy các test suite trên nhiều thiết bị đồng thời bằng cách cấu hình
detox test -c ios.sim.debug -c android.emu.debug. - Mock network: Dùng
detoxkết hợp vớimsw(Mock Service Worker) để tránh các cuộc gọi mạng thực tế, giảm độ trễ.
Integrate Detox vào pipeline CI/CD
GitHub Actions workflow mẫu
Dưới đây là một workflow chạy test E2E trên macOS (cần để build iOS) và Ubuntu (cho Android). Lưu ý rằng việc cài đặt Xcode và Android SDK được thực hiện bằng các action có sẵn.
name: Detox E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Install pods
run: cd ios && pod install && cd ..
- name: Build iOS app
run: npx detox build -c ios.sim.debug
- name: Run Detox iOS tests
run: npx detox test -c ios.sim.debug --reuse
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Setup Android SDK
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 33
target: google_apis
arch: x86_64
force-avd-creation: true
- name: Build Android app
run: npx detox build -c android.emu.debug
- name: Run Detox Android tests
run: npx detox test -c android.emu.debug --reuseWorkflow trên thực hiện các bước:
- Checkout mã nguồn.
- Cài đặt Node.js và các phụ thuộc.
- Biên dịch ứng dụng cho từng nền tảng.
- Chạy test Detox trên thiết bị ảo.
Khi một pull request được mở, các test sẽ tự động chạy, ngăn chặn việc merge code lỗi.
Best practices khi sử dụng Detox
- Đặt testID nhất quán: Sử dụng một chuẩn đặt tên (ví dụ
loginButton,profileScreen) để tránh nhầm lẫn. - Tránh phụ thuộc vào thời gian: Thay vì
await waitFor(...).toBeVisible().withTimeout(5000), hãy dùngawait expect(...).toBeVisible()để Detox tự chờ tới khi UI sẵn sàng. - Mock dữ liệu ngoại vi: Sử dụng
jest.mockhoặcmswđể trả về dữ liệu cố định, giảm biến động test. - Giữ test độc lập: Mỗi test case nên khởi tạo lại trạng thái (đăng xuất, xóa dữ liệu tạm) để không gây ảnh hưởng lẫn nhau.
- Chạy test trên thiết bị thật: Khi có thể, chạy một phần test trên thiết bị thực để phát hiện các vấn đề liên quan tới hiệu năng hoặc permission.
Kết luận
Detox cung cấp một giải pháp mạnh mẽ cho kiểm thử end‑to‑end trong dự án React Native, giúp phát hiện sớm các lỗi giao diện và luồng người dùng trên cả iOS và Android. Bằng cách thiết lập cấu hình chuẩn, viết test có testID rõ ràng, tối ưu hoá thời gian chạy và tích hợp vào CI/CD, bạn có thể nâng cao chất lượng sản phẩm và giảm thiểu rủi ro khi phát hành.
Để nắm vững toàn bộ quy trình phát triển ứng dụng di động từ cơ bản tới chuyên sâu, Tham khảo khóa học "Lập trình App Mobile với React Native + TypeScript" tại đây.







