Mở đầu — vì sao bài này quan trọng
Ở Bài 1, bạn đã có cái nhìn tổng quan về testing di động. Nhưng khi bắt tay vào việc thật, câu hỏi đầu tiên mà bất kỳ QA nào cũng phải trả lời là: "Tôi sẽ viết test case gì cho ứng dụng mobile này?" Và ngay sau đó là: "Làm sao để một phần trong số đó chạy tự động, không phải mở app bấm tay mỗi lần build?"
Đây chính là nội dung của Bài 2. Chúng ta sẽ học cách thiết kế test case cho mobile — đặc biệt là những kịch bản mà bạn KHÔNG gặp trên web như cử chỉ chạm (gestures), xoay màn hình, ngắt kết nối giữa chừng — và làm quen với Appium, công cụ automation phổ biến nhất cho di động.
Vì sao phần này quan trọng đến vậy? Bởi vì test case là "bản thiết kế" của toàn bộ công việc QA. Một QA giỏi không phải người bấm app nhanh nhất, mà là người nghĩ ra được những kịch bản mà lập trình viên và cả PM đều quên mất. Trên mobile, số lượng những kịch bản "bị quên" nhiều gấp bội so với web: người dùng vuốt sai hướng, xoay ngang màn hình đúng lúc đang thanh toán, hay bấm long-press vào một nút mà designer chưa từng nghĩ ai sẽ giữ lâu như vậy. Nếu bạn không viết ra thành test case, chúng sẽ thành bug ngoài production.
Lưu ý: bài này tập trung vào thiết kế test case cho gestures/tương tác đặc thù mobile và kiến thức nền tảng để bắt đầu với Appium. Các phần chuyên sâu như kiến trúc Appium, locator strategy, hay viết flow login hoàn chỉnh sẽ được đào sâu ở các bài sau (Bài 7–10). Ở đây, bạn cần nắm được bức tranh và biết mình đang đứng ở đâu.
Khái niệm cốt lõi
Test case cho mobile khác gì so với web?
Trên web, phần lớn tương tác là click và nhập text. Trên mobile, "ngôn ngữ" tương tác phong phú hơn rất nhiều. Một test case mobile tốt phải mô tả rõ:
- Precondition (điều kiện trước): trạng thái app, dữ liệu, mạng, quyền (permission) trước khi test.
- Steps (các bước): mô tả CHÍNH XÁC cử chỉ, không chỉ là "bấm nút" mà là "vuốt sang trái từ mép phải màn hình".
- Expected result (kết quả mong đợi): điều gì phải xảy ra, kèm cả phản hồi hình ảnh/haptic nếu có.
- Postcondition: trạng thái sau khi test, để dọn dẹp cho test tiếp theo.
Các cử chỉ (gestures) then chốt cần kiểm thử
Đây là trái tim của test case tương tác mobile. Với mỗi gesture, bạn cần nghĩ đến cả trường hợp đúng và trường hợp biên (edge case):
Tap (chạm) — hành động cơ bản nhất. Nhưng đừng chủ quan: cần test tap chính xác vào vùng chạm (touch target đủ lớn, tối thiểu ~44x44pt trên iOS, ~48dp trên Android), tap nhanh liên tục (double-tap nhầm gây mua hàng hai lần), và tap vào vùng đang loading.
Double tap (chạm đúp) — thường dùng để zoom ảnh hoặc "thả tim" (như Instagram, TikTok). Test case: double-tap có kích hoạt đúng hành động không, và có bị hiểu nhầm thành hai single-tap không.
Long press (nhấn giữ) — mở menu ngữ cảnh, kéo-thả, xem trước (preview). Cần test thời gian giữ (bao lâu thì kích hoạt), và điều gì xảy ra khi giữ rồi nhấc tay ra ngoài vùng.
Swipe (vuốt: trái, phải, lên, xuống) — vuốt để xóa item trong danh sách, chuyển tab, mở menu. Đây là nguồn bug kinh điển: vuốt nửa chừng rồi thả ra, vuốt quá nhanh, vuốt xung đột với scroll.
Pinch to zoom (chụm để phóng to) — bản đồ, ảnh, tài liệu. Test giới hạn zoom (min/max), và zoom rồi xoay màn hình.
Pull to refresh (kéo xuống để làm mới) — kéo từ đỉnh danh sách để tải lại. Test: kéo khi đang offline, kéo liên tục nhiều lần, kéo khi danh sách rỗng.
Appium là gì và nó nằm ở đâu trong bức tranh
Appium là một công cụ mã nguồn mở giúp bạn viết test tự động cho ứng dụng mobile — chạy được trên cả iOS và Android, với cùng một bộ code test. Ý tưởng cốt lõi của Appium rất hay: nó tuân theo chuẩn WebDriver (giống Selenium bên web), nên bạn dùng một API quen thuộc để "điều khiển" điện thoại như thể đang ra lệnh cho một trình duyệt.
Bạn viết test bằng ngôn ngữ mình thích (Java, Python, JavaScript...), gửi lệnh đến Appium Server, server này dịch lệnh xuống các framework gốc của từng nền tảng (XCUITest cho iOS, UiAutomator2 cho Android) để thực thi trên thiết bị/giả lập. Bạn không cần biết chi tiết bên trong ngay lúc này — chỉ cần hiểu rằng Appium là "cầu nối" giữa test code của bạn và thiết bị thật.
Một khái niệm nền tảng bạn sẽ gặp ngay từ đầu là Desired Capabilities (khả năng mong muốn) — một tập cấu hình mô tả bạn muốn test trên thiết bị nào, hệ điều hành phiên bản gì, app nào. Ví dụ tối giản:
{
"platformName": "Android",
"appium:deviceName": "Pixel_6_API_33",
"appium:automationName": "UiAutomator2",
"appium:app": "/path/to/app-debug.apk"
}
Và một dòng lệnh gesture đơn giản trong Appium (Python) trông như thế này:
# Tap vào nút "Đăng nhập"
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "btn_login").click()
# Swipe từ dưới lên (cuộn danh sách)
driver.swipe(start_x=500, start_y=1500, end_x=500, end_y=500, duration=800)
Ở bài này chúng ta chỉ cần "chạm mặt" Appium để bạn hình dung được test case sẽ được biến thành automation ra sao. Chi tiết setup, locator, và flow đầy đủ sẽ ở Bài 7–10.
Tình huống thực tế
Tình huống 1 — Vuốt-để-xóa gây mất đơn hàng ở một app giao đồ ăn
Một đội QA làm cho ứng dụng giao đồ ăn kiểu ShopeeFood/GrabFood (giả định tên nội bộ là "FoodGo") có màn hình giỏ hàng, mỗi món ăn là một dòng, vuốt sang trái để xóa. Bản đầu tiên qua test thủ công "bấm nút xóa" ngon lành. Nhưng khi lên production, tỷ lệ khiếu nại "tự nhiên mất món trong giỏ" tăng vọt.
Điều tra ra: khi người dùng vuốt để cuộn danh sách giỏ hàng mà tay hơi lệch ngang, hệ thống hiểu nhầm thành swipe-to-delete, kích hoạt xóa mà không kịp hiện xác nhận. Test case ban đầu chỉ có "bấm nút xóa", thiếu hẳn nhóm case về swipe.
Bài học: test case gesture phải bao gồm cả gesture xung đột — swipe ngang vs scroll dọc, và nút xóa phải luôn kèm bước Undo/xác nhận. Đội QA sau đó bổ sung 8 test case swipe (vuốt chậm, vuốt nhanh, vuốt chéo, vuốt nửa chừng thả ra...) và tự động hóa 5 case bằng Appium để chạy mỗi lần build.
Tình huống 2 — Double-tap "thả tim" bị đếm nhầm ở một app mạng xã hội
Một startup mạng xã hội tại TP.HCM có tính năng double-tap để "thả tim" bài viết, giống Instagram. QA viết test case: "double-tap → tim xuất hiện, số like +1". Chạy ổn.
Vấn đề xuất hiện khi người dùng thật double-tap rất nhanh trên máy cấu hình yếu (nhiều máy Android tầm trung phổ biến ở Việt Nam): đôi khi hệ thống đếm thành 2 lượt like rồi -1, gây nhấp nháy con số. Nguyên nhân là test chỉ chạy trên giả lập mạnh, timing hoàn hảo, nên không lộ ra.
Bài học: test case gesture cần đi kèm điều kiện thiết bị (device matrix) và timing biên. Đội đã thêm case "double-tap với khoảng cách thời gian ngắn nhất/dài nhất có thể" và chạy trên cả máy yếu. Với Appium, họ dùng TouchAction/W3C Actions để mô phỏng double-tap ở nhiều tốc độ khác nhau — điều gần như không thể làm nhất quán bằng tay.
Tình huống 3 — Pull-to-refresh khi offline làm treo màn hình ví điện tử
Một ví điện tử (giả định "PayViet") có màn hình lịch sử giao dịch, kéo xuống để làm mới. QA test khi có mạng: mọi thứ tốt. Nhưng khi người dùng ở khu vực sóng yếu (thang máy, hầm gửi xe) kéo-làm-mới, spinner quay mãi không dừng, màn hình như bị treo, người dùng hoảng vì tưởng ví lỗi tiền.
Điều tra: request bị timeout nhưng UI không xử lý trạng thái lỗi cho hành động pull-to-refresh, spinner không được ẩn đi. Test case gốc thiếu điều kiện mạng.
Bài học: mọi gesture kích hoạt network (pull-to-refresh, infinite scroll...) đều cần test case cho offline / mạng chậm / timeout. Đây là kịch bản cực kỳ phổ biến ở Việt Nam do hạ tầng mạng không đồng đều. Đội bổ sung test case "kéo làm mới khi offline → hiện thông báo lỗi + spinner dừng trong ≤ 5 giây".
Hướng dẫn từng bước
Dưới đây là quy trình thực tế để đi từ ý tưởng đến một bộ test case gesture có thể tự động hóa:
Bước 1 — Liệt kê mọi tương tác trên màn hình. Mở từng màn hình, ghi ra tất cả điểm chạm được và cử chỉ hỗ trợ: nút này tap được gì, danh sách này vuốt được không, ảnh này có zoom không. Đừng bỏ sót "cử chỉ ẩn" như vuốt cạnh màn hình để back.
Bước 2 — Với mỗi gesture, viết cả case chính và case biên. Áp dụng khung: happy path (đúng như thiết kế), edge (nửa chừng, quá nhanh, sai hướng), interrupt (có cuộc gọi/thông báo chen vào), context (offline, xoay màn hình, app vào background).
Bước 3 — Chuẩn hóa format test case. Mỗi case gồm: ID, tiêu đề, precondition, steps, expected result. Ví dụ:
> TC-CART-07 — Vuốt trái để xóa món khi mạng chậm > Precondition: giỏ có 3 món, mạng giả lập 3G chậm. > Steps: 1) Vuốt trái item thứ 2 hết hành trình. 2) Bấm "Xóa". > Expected: Hiện xác nhận; sau khi xác nhận, món biến mất, tổng tiền cập nhật đúng, có nút Undo trong 5s.
Bước 4 — Đánh dấu case nào nên tự động hóa. Ưu tiên tự động những case chạy lặp lại mỗi build, ổn định và tốn công làm tay (như happy path đăng nhập, thêm/xóa giỏ). Giữ lại thủ công những case cần cảm nhận trực quan (mượt của animation, độ nhạy haptic).
Bước 5 — Chuyển case ưu tiên thành Appium script. Với mỗi case tự động: xác định element (bằng accessibility id là tốt nhất), viết action (click/swipe/W3C Actions), rồi assert kết quả. Bắt đầu từ 1–2 case đơn giản để làm quen, đừng ôm cả bộ ngay.
Bước 6 — Chạy trên device matrix. Đảm bảo test chạy ít nhất trên một máy iOS, một máy Android mạnh, và một máy Android tầm trung/yếu — vì như tình huống 2 đã cho thấy, bug timing chỉ lộ ra trên máy yếu.
Lỗi thường gặp & mẹo
Lỗi 1: Chỉ test "happy path" của gesture. Rất nhiều QA mới chỉ viết "tap nút → thành công". Bug thật nằm ở nửa chừng, sai hướng, và ngắt quãng. Mẹo: với mỗi gesture, luôn tự hỏi ba câu — "nếu làm nửa chừng?", "nếu làm quá nhanh?", "nếu bị chen ngang?".
Lỗi 2: Dùng tọa độ tuyệt đối (x, y cứng) khi swipe trong Appium. Tọa độ cứng sẽ vỡ trên màn hình kích thước khác. Mẹo: tính tọa độ theo tỷ lệ kích thước màn hình (size = driver.get_window_size() rồi nhân tỷ lệ), hoặc dùng W3C Actions bám vào element thay vì điểm cố định.
Lỗi 3: Nhầm giữa double-tap và hai single-tap. Khi tự động hóa, nếu để khoảng nghỉ quá dài giữa hai lần tap, framework hiểu thành hai tap riêng. Mẹo: dùng W3C Actions với pause ngắn có kiểm soát, và test cả ngưỡng thời gian mà app coi là double-tap.
Lỗi 4: Bỏ quên touch target quá nhỏ. Nút đẹp nhưng vùng chạm nhỏ hơn chuẩn (44pt iOS / 48dp Android) sẽ khiến người dùng tap trượt. Mẹo: đưa kiểm tra kích thước touch target thành một mục cố định trong checklist test case.
Lỗi 5: Không dọn trạng thái giữa các test (test bẩn). Test xóa món chạy trước làm giỏ hàng rỗng, khiến test sau thất bại "không tìm thấy món". Mẹo: luôn viết precondition rõ ràng và reset dữ liệu ở đầu mỗi case tự động.
Mẹo vàng: ưu tiên accessibility id làm locator khi lập trình viên đặt sẵn. Nó vừa ổn định cho automation, vừa phục vụ cả kiểm thử tiếp cận (a11y) — một mũi tên trúng hai đích. Hãy đề nghị dev thêm accessibilityLabel/contentDescription ngay từ khâu phát triển.
Bài tập thực hành
- Lập bảng gesture matrix. Chọn một app bạn hay dùng (ví dụ ShopeeFood, Zalo, hoặc app ngân hàng). Chọn một màn hình có danh sách. Liệt kê tất cả gesture màn hình đó hỗ trợ (tap, long-press, swipe, pull-to-refresh...). Với mỗi gesture, viết ít nhất 1 happy case và 2 edge case theo format ID/precondition/steps/expected.
- Viết test case cho tình huống xung đột. Thiết kế một test case mô tả rõ trường hợp "vuốt cuộn danh sách bị hiểu nhầm thành vuốt-xóa" (như tình huống 1). Ghi rõ kết quả mong đợi để đảm bảo app không xóa nhầm.
- Phác thảo Appium script (giả mã). Với case happy-path "tap nút Đăng nhập rồi kiểm tra chuyển sang màn hình chính", viết ra các bước dưới dạng giả mã Appium: desired capabilities cần gì, tìm element bằng gì, action gì, assert gì. Chưa cần chạy được — mục tiêu là quen với luồng suy nghĩ.
- Điều kiện mạng. Lấy một gesture có gọi network (pull-to-refresh hoặc infinite scroll), viết 3 test case cho: mạng bình thường, mạng chậm (3G), và offline. Ghi rõ hành vi mong đợi cho từng trường hợp.
Tóm tắt
Test case cho mobile không chỉ là "bấm và kiểm tra" — nó là nghệ thuật nghĩ ra những kịch bản tương tác mà người khác bỏ quên. Trọng tâm của bài này là nhóm gestures: tap, double-tap, long-press, swipe bốn hướng, pinch-to-zoom, pull-to-refresh — và với mỗi cái, bạn phải phủ cả happy path, edge case (nửa chừng, quá nhanh, sai hướng), lẫn context động (offline, ngắt quãng, xoay màn hình, máy yếu).
Ba tình huống thực tế — mất đơn hàng do swipe nhầm, đếm sai like do double-tap trên máy yếu, và treo màn hình pull-to-refresh khi offline — cho thấy các bug nghiêm trọng nhất thường đến từ chính những gesture bị test hời hợt.
Về automation, bạn đã làm quen với Appium: một công cụ theo chuẩn WebDriver, dùng Desired Capabilities để cấu hình thiết bị, và biến test case thành code chạy được trên cả iOS lẫn Android. Ở bài này ta mới chạm mặt; kiến trúc, locator strategy và flow đầy đủ sẽ được đào sâu ở các bài tiếp theo.
Nguyên tắc cần khắc cốt: một test case tốt bắt đầu từ câu hỏi "nếu điều này xảy ra giữa chừng thì sao?" Hãy mang tư duy đó vào mọi màn hình bạn kiểm thử.