Mở đầu — vì sao bài này quan trọng
Ở Việt Nam, có một sự thật mà bất kỳ ai làm QA cũng cần thấm: người dùng của bạn sống trên điện thoại, không phải trên máy tính. Theo các báo cáo thị trường số Việt Nam những năm gần đây, hơn 70% lưu lượng truy cập của các nền tảng như Shopee, Tiki, MoMo, VNPay đến từ ứng dụng di động. Khi bạn đặt Grab, chuyển tiền qua MoMo, hay lướt TikTok Shop — bạn đang dùng app native trên điện thoại, không phải trình duyệt.
Vấn đề là: những gì bạn đã học ở các bài trước — Selenium WebDriver để tự động hóa web — không chạy được trên app di động native. Selenium chỉ nói chuyện được với trình duyệt. Còn một app Android viết bằng Kotlin/Java hay một app iOS viết bằng Swift là một thế giới hoàn toàn khác: không có DOM, không có document.querySelector, mà là các thành phần UI native của hệ điều hành.
Đây chính là chỗ Appium bước vào. Appium cho phép bạn tự động hóa app di động — cả Android lẫn iOS — bằng cùng một bộ API mà bạn đã quen từ Selenium. Nếu bạn từng viết driver.findElement(...) cho web, bạn sẽ thấy Appium quen thuộc đến bất ngờ. Bài này sẽ giúp bạn hiểu Appium hoạt động ra sao, dựng được môi trường đầu tiên, và viết được test case đầu tiên chạy trên một máy ảo hoặc điện thoại thật.
Khái niệm cốt lõi
Appium là gì?
Appium là một công cụ mã nguồn mở (open-source) dùng để tự động hóa kiểm thử ứng dụng di động. Điểm mạnh cốt lõi của nó nằm ở một triết lý: "Viết test một lần, chạy trên nhiều nền tảng". Bạn dùng cùng một API dựa trên chuẩn WebDriver Protocol (chính xác là chuẩn W3C WebDriver — cùng chuẩn mà Selenium dùng) để điều khiển cả iOS lẫn Android.
Ba đặc điểm khiến Appium được ưa chuộng trong ngành:
- Cross-platform: một kỹ năng, hai nền tảng. Team của bạn không cần thuê riêng người biết công cụ Android và người biết công cụ iOS.
- Không cần sửa app: Appium không yêu cầu bạn nhúng thư viện hay SDK nào vào bên trong app. Bạn test đúng cái file
.apk(Android) hoặc.ipa(iOS) mà người dùng thật sẽ cài — đây gọi là kiểm thử "black-box". - Đa ngôn ngữ: vì Appium giao tiếp qua HTTP theo chuẩn WebDriver, bạn có thể viết test bằng Java, Python, JavaScript, C#, Ruby... tùy team.
Ba loại app mà Appium test được
Đây là kiến thức nền quan trọng, vì nó quyết định cách bạn viết locator:
- Native app: app viết thuần bằng công nghệ của nền tảng (Kotlin/Java cho Android, Swift/Objective-C cho iOS). Ví dụ: app MoMo, app Vietcombank.
- Hybrid app: app native "bọc" một WebView bên trong (thường dùng công nghệ như Cordova, Ionic). Bên ngoài là native, bên trong một số màn hình là web.
- Mobile web app: chính là website mở trong trình duyệt di động (Chrome trên Android, Safari trên iOS). Với loại này Appium hành xử gần giống Selenium.
Kiến trúc Appium — hiểu để không "mù" khi debug
Rất nhiều bạn mới học Appium bị "loạn" vì không hiểu luồng dữ liệu đi qua đâu. Hãy nhìn kiến trúc theo mô hình client–server:
Test Script (Java/Python...)
│ gửi lệnh HTTP (JSON theo chuẩn W3C WebDriver)
▼
Appium Server (Node.js)
│ chọn "driver" phù hợp với nền tảng
▼
┌───────────────┬────────────────┐
│ UiAutomator2 │ XCUITest │
│ (Android) │ (iOS) │
└───────────────┴────────────────┘
│ │
▼ ▼
Thiết bị/Emulator Android Thiết bị/Simulator iOS
Diễn giải luồng này:
- Test script của bạn (client) gửi một lệnh, ví dụ "bấm vào nút Đăng nhập", dưới dạng một HTTP request tới Appium Server.
- Appium Server là một chương trình Node.js đóng vai trò trung gian. Nó nhận request và chuyển tiếp cho đúng driver.
- Driver là phần chuyển ngữ lệnh của Appium thành lệnh mà hệ điều hành hiểu. Trên Android, driver mặc định là UiAutomator2 (dựa trên framework kiểm thử của Google). Trên iOS là XCUITest (dựa trên framework của Apple).
- Driver thực thi hành động thật trên thiết bị hoặc máy ảo (emulator cho Android, simulator cho iOS), rồi trả kết quả ngược lại chuỗi trên.
Desired Capabilities — "tờ khai" trước mỗi phiên test
Trước khi Appium bắt đầu một session, bạn phải khai báo một tập cấu hình gọi là capabilities (phiên bản mới gọi là options). Đây là "tờ khai" nói cho Appium biết: bạn muốn test nền tảng nào, thiết bị nào, dùng driver gì, và app nào.
options = {
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "Pixel_6_API_33",
"appium:app": "/duong-dan/toi/app.apk",
"appium:appPackage": "vn.momo.app",
"appium:appActivity": ".MainActivity"
}
Tình huống thực tế
Ví dụ 1: Đội QA của một ví điện tử Việt Nam và bài toán regression
Hãy tưởng tượng "PayViet" — một ví điện tử giả định tương tự MoMo — có app trên cả Android và iOS. Mỗi 2 tuần họ ra một bản cập nhật. Luồng "nạp tiền vào ví" gồm 8 bước: mở app → đăng nhập → chọn Nạp tiền → chọn ngân hàng → nhập số tiền → xác nhận OTP → thấy màn hình thành công → kiểm tra số dư.
Ban đầu, đội QA gồm 6 người test tay luồng này trên 12 mẫu điện thoại khác nhau (từ iPhone đời cũ tới Samsung, Xiaomi giá rẻ) sau mỗi bản build. Mỗi vòng regression tốn 1,5 ngày công. Với tần suất 2 tuần/lần, họ đốt gần 20% thời gian chỉ để lặp lại các thao tác giống hệt nhau.
Họ dùng Appium tự động hóa 15 luồng nghiệp vụ quan trọng nhất. Kết quả sau 3 tháng: vòng regression trên máy Android chạy tự động qua đêm chỉ còn 40 phút, đội QA sáng hôm sau chỉ cần đọc báo cáo. Con người được giải phóng để tập trung vào test khám phá (exploratory testing) những tính năng mới.
Bài học rút ra: Appium tỏa sáng nhất ở những luồng lặp đi lặp lại và ổn định. Đừng cố tự động hóa mọi thứ ngay từ đầu — hãy chọn các "happy path" nghiệp vụ cốt lõi có tần suất chạy cao.
Ví dụ 2: Startup thương mại điện tử và cái bẫy "test iOS trên máy Windows"
Một startup e-commerce ở TP.HCM, đội kỹ thuật khoảng 15 người, quyết định làm automation cho app mua sắm. Bạn QA phụ trách được cấp một máy laptop Windows. Bạn ấy dựng Appium thành công cho Android trong 2 ngày, test chạy ngon lành. Nhưng khi tới lượt iOS thì tắc hoàn toàn — báo lỗi không tìm thấy Xcode.
Sau nửa ngày loay hoay, cả team mới nhận ra một giới hạn nền tảng không thể vượt qua: driver XCUITest cho iOS chỉ chạy trên macOS, vì nó phụ thuộc vào Xcode của Apple. Không có cách nào chạy test iOS thật trên Windows. Cuối cùng công ty phải mua một máy Mac mini để làm "máy build iOS", hoặc thuê dịch vụ cloud (chủ đề của một bài học khác trong khóa).
Bài học rút ra: Trước khi bắt đầu dự án Appium, hãy làm rõ phạm vi nền tảng và phần cứng cần thiết. iOS = bắt buộc macOS + Xcode. Đây là ràng buộc kỹ thuật, không phải lỗi cấu hình — biết trước sẽ tiết kiệm cho bạn nhiều ngày công và tránh hứa hẹn sai với sếp.
Ví dụ 3: App giao đồ ăn và "phần tử biến mất"
Một đội QA làm app giao đồ ăn kiểu ShopeeFood viết test tự động cho luồng đặt món. Test chạy tốt trên máy của lập trình viên nhưng cứ thi thoảng thất bại trên máy CI (máy chạy build tự động): lỗi NoSuchElementException ở bước bấm nút "Thêm vào giỏ".
Điều tra ra thì nguyên nhân là tính bất định của thời gian tải. Trên máy CI (thường yếu hơn, tải nhiều thứ cùng lúc), màn hình danh sách món ăn tải chậm hơn vài trăm mili-giây. Test đã cố bấm nút trước khi nút kịp hiển thị. Giải pháp là dùng explicit wait — chờ cho phần tử xuất hiện rồi mới thao tác, thay vì giả định nó có sẵn ngay.
Bài học rút ra: Trên di động, độ trễ mạng, độ trễ render animation, và độ mạnh yếu của thiết bị làm cho thời điểm phần tử xuất hiện không cố định. Đây là nguyên nhân số một gây ra test "flaky" (chập chờn) trong mobile automation.
Hướng dẫn từng bước
Dưới đây là lộ trình dựng môi trường và viết test đầu tiên cho Android (nền tảng dễ bắt đầu nhất vì chạy được trên cả Windows/Mac/Linux). Ta dùng Python cho ngắn gọn.
Bước 1 — Cài đặt nền tảng cần thiết.
- Cài Node.js (Appium Server chạy trên Node).
- Cài Java JDK và Android SDK (thường qua Android Studio). Đặt biến môi trường
ANDROID_HOMEtrỏ tới thư mục SDK. - Tạo một máy ảo Android qua AVD Manager trong Android Studio, hoặc cắm điện thoại thật đã bật chế độ USB Debugging (trong phần Developer Options).
npm install -g appium
appium driver install uiautomator2
Lệnh đầu cài Appium. Lệnh sau cài driver Android. Kiểm tra môi trường đã ổn chưa bằng công cụ chẩn đoán:
npm install -g appium-doctor
appium-doctor --android
appium-doctor sẽ liệt kê từng thứ còn thiếu (JAVA_HOME, ANDROID_HOME...) — cực kỳ hữu ích cho người mới.Bước 3 — Khởi động Appium Server.
appium
Server sẽ chạy mặc định ở http://127.0.0.1:4723. Cửa sổ này phải luôn mở khi bạn chạy test.Bước 4 — Cài thư viện client.
pip install Appium-Python-Client
Bước 5 — Viết test đầu tiên. Ví dụ mở app Cài đặt (Settings) có sẵn trên mọi máy Android và kiểm tra nó mở được:
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECoptions = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "emulator-5554"
options.app_package = "com.android.settings"
options.app_activity = ".Settings"
driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
Chờ tối đa 10 giây cho phần tử "Network & internet" xuất hiện rồi bấm
wait = WebDriverWait(driver, 10)
element = wait.until(EC.presence_of_element_located(
(AppiumBy.XPATH, "//*[contains(@text, 'Network')]")
))
element.click()driver.quit()
Bước 6 — Tìm locator bằng Appium Inspector.
Đây là công cụ phải có. Appium Inspector là ứng dụng đồ họa cho phép bạn kết nối tới session đang chạy, chụp lại cây UI của app, click vào một phần tử để xem resource-id, text, content-desc, xpath của nó. Nó chính là "DevTools cho app di động". Trên Android, locator tốt nhất thường là accessibility id (tức content-desc) hoặc resource-id, vì chúng ổn định hơn XPath.
Bước 7 — Chạy và quan sát. Chạy file Python. Bạn sẽ thấy emulator tự mở app Settings và tự bấm vào mục Network. Xin chúc mừng — bạn vừa viết test Appium đầu tiên.
Lỗi thường gặp & mẹo
1. Quên khởi động Appium Server. Test báo lỗi kết nối Connection refused tới cổng 4723. Nhớ: Appium là mô hình client-server, server phải chạy trước.
2. Lạm dụng XPath dài và mong manh. Nhiều bạn copy nguyên chuỗi XPath dài ngoằng từ Inspector, kiểu //android.widget.LinearLayout[3]/android.widget.Button[1]. Chỉ cần lập trình viên đổi layout một chút là test gãy. Mẹo: ưu tiên accessibility id và resource-id. Nếu app của bạn thiếu các thuộc tính này, hãy đề nghị đội dev thêm content-desc/testID vào các phần tử — đây là "shift-left", làm app dễ test hơn ngay từ gốc.
3. Không dùng wait, dùng sleep cứng. Nhiều người thấy test flaky liền thêm time.sleep(5) khắp nơi. Cách này vừa chậm (luôn đợi đủ 5 giây kể cả khi phần tử đã sẵn sàng) vừa không đáng tin. Hãy dùng explicit wait như trong ví dụ Bước 5.
4. Nhầm giữa appPackage/appActivity và đường dẫn .apk. Nếu app đã cài trên máy, bạn khai appPackage + appActivity để mở nó. Nếu chưa cài, bạn khai app trỏ tới file .apk để Appium tự cài. Tìm package name bằng lệnh adb shell dumpsys window | grep -i mCurrentFocus khi app đang mở.
5. Với hybrid app, quên chuyển context. Mặc định Appium đang ở context NATIVE_APP. Khi cần thao tác phần WebView, bạn phải chuyển sang context web bằng driver.switch_to.context('WEBVIEW_...'). Nhiều bạn tìm mãi không thấy phần tử bên trong WebView chỉ vì quên bước này.
6. Mẹo dùng adb là bạn thân. adb devices để xem thiết bị đang kết nối, adb logcat để đọc log app khi debug. Với người làm Appium trên Android, adb là công cụ không thể thiếu.
Bài tập thực hành
- Dựng môi trường: Cài Node.js, Android Studio, Appium, driver UiAutomator2. Chạy
appium-doctor --androidcho đến khi tất cả mục quan trọng đều màu xanh. Tạo một emulator Android.
- Test cơ bản: Viết một test mở app Máy tính (Calculator) hoặc Cài đặt có sẵn trên emulator. Dùng Appium Inspector để tìm locator cho ít nhất 3 phần tử.
- Kịch bản có luồng: Viết test thực hiện phép tính "5 + 3 = 8" trên app Calculator: bấm 5, bấm +, bấm 3, bấm =, rồi kiểm tra (assert) kết quả hiển thị là 8. Bắt buộc dùng explicit wait cho ít nhất một phần tử.
- Nâng cao — so sánh locator: Với cùng một nút, thử tìm nó bằng 3 cách: XPath, resource-id, và accessibility id. Ghi lại cách nào ngắn gọn và ổn định nhất theo cảm nhận của bạn.
- Suy ngẫm: Viết ra 3 luồng nghiệp vụ trong một app bạn hay dùng (Grab, MoMo, Shopee) mà bạn cho là đáng tự động hóa nhất, và giải thích vì sao (gợi ý: dựa trên tần suất chạy và độ ổn định của luồng).
Tóm tắt
Appium là công cụ mã nguồn mở giúp bạn tự động hóa kiểm thử app di động trên cả Android và iOS bằng cùng một API dựa trên chuẩn W3C WebDriver — chính là bộ kỹ năng bạn đã có từ Selenium, nay mở rộng sang thế giới mobile. Kiến trúc của nó là mô hình client–server: test script gửi lệnh HTTP tới Appium Server, server chuyển tới driver phù hợp (UiAutomator2 cho Android, XCUITest cho iOS), và driver thực thi trên thiết bị hoặc máy ảo.
Ba điều cốt lõi cần khắc ghi: (1) Appium test được native, hybrid và mobile web app mà không cần sửa app; (2) test iOS bắt buộc phải có macOS và Xcode — đây là ràng buộc nền tảng; (3) kẻ thù lớn nhất của bạn là test flaky, và vũ khí chống lại nó là explicit wait cùng locator ổn định (ưu tiên accessibility id, resource-id thay vì XPath mong manh).
Ở bối cảnh Việt Nam — nơi phần lớn người dùng sống trên điện thoại — mobile automation không phải là "tính năng thêm nếm" mà là năng lực cốt lõi của một đội QA hiện đại. Hãy bắt đầu nhỏ: dựng môi trường Android, viết một test chạy được, rồi mở rộng dần sang các luồng nghiệp vụ thật của sản phẩm bạn.