AllianceProject Handbook
← Knowledge

Quy mô và hiệu năng

Test runtime permission thế nào

Bài kiểm thử đặc thù mobile nói mỗi quyền phải test đủ sáu trạng thái. Bài này nói đưa quyền về từng trạng thái bằng lệnh gì, vì sao Android không phân biệt được "chưa hỏi" với "deny vĩnh viễn", và phần nào máy chặn được.

Cập nhật 14/09/2026kiểm thửmobileshift left

Kiểm thử đặc thù mobile liệt kê sáu trạng thái mỗi quyền phải đi qua. Bài đó dừng ở phải làm gì. Bài này là phần làm bằng cách nào — lệnh đưa quyền về từng trạng thái, một cái bẫy của Android làm app xử lý sai trạng thái ngay từ đầu, và phần nhỏ mà máy chặn được.

Đưa quyền về từng trạng thái bằng lệnh gì

Không trạng thái nào cần gỡ app cài lại. Gỡ cài lại làm mất luôn dữ liệu local, tức là đổi sang một kịch bản khác mà không biết.

Android
iOS
Về đúng như mới cài
adb shell pm reset-permissions
xcrun simctl privacy <device> reset all <bundle-id>
Cấp sẵn một quyền
adb shell pm grant <package> android.permission.CAMERA
xcrun simctl privacy <device> grant camera <bundle-id>
Thu hồi khi app đang chạy
adb shell pm revoke <package> <permission> — lệnh này giết luôn tiến trình app, đó là hành vi thật của hệ thống
xcrun simctl privacy <device> revoke camera <bundle-id>
Deny vĩnh viễn
Bấm Deny hai lần liên tiếp trên cùng một quyền
Bấm Don't Allow một lần — iOS không hỏi lại lần nào nữa
Allow once
Chọn Only this time ở location, camera, microphone
Chọn Allow Once
Hệ thống tự reset sau thời gian dài không dùng
Android tự thu hồi quyền của app lâu không mở; mô phỏng bằng pm reset-permissions
iOS không tự reset, đặt lại tay ở Settings → General → Transfer or Reset

Android không phân biệt được "chưa hỏi" với "deny vĩnh viễn"

Đây là chỗ app xử lý sai ngay từ dòng code đầu tiên, và không có test nào bắt được nếu không biết trước.

  1. Chưa hỏi lần nào

    hàm kiểm tra "có nên giải thích không" trả về false

  2. Đã hỏi, user deny một lần

    trả về true — đây là lúc app giải thích rồi hỏi lại

  3. User deny lần thứ hai

    trả về false trở lại — giống hệt trạng thái chưa hỏi lần nào

iOS không có bẫy này

iOS trả về trạng thái riêng cho "chưa quyết định" và "đã từ chối". Code hai nền tảng vì thế không đối xứng — đừng bê logic iOS sang Android rồi tin là xong.

Vì sao đáng để làm đúng

Tỷ lệ deny vĩnh viễn của Notification 5% ở 500.000 user là 25.000 người không bao giờ nhận được nhắc hạn Task. Nút bấm không phản ứng đẩy con số đó lên, vì user bấm vài lần rồi bỏ.

Sáu trạng thái, chạy bằng người, cho từng quyền

Chạy trọn vòng cho từng quyền một. Trộn hai quyền trong một vòng thì không biết trạng thái nào gây ra kết quả nào.

  1. Reset về như mới cài

    pm reset-permissions, rồi mở app từ launcher.

  2. Đi tới đúng chức năng cần quyền

    Ghi lại dialog hiện ra ở màn hình nào. Hiện ở màn hình đầu tiên là không đạt, dừng luôn, không cần chạy tiếp.

  3. Deny lần một

    App phải giải thích vì sao cần và còn đường thử lại. Im lặng quay về là không đạt.

  4. Deny lần hai

    App không được hỏi lại vô ích. Phải chỉ đường ra Settings, và đường đó phải bấm được chứ không phải một câu chữ.

  5. Cấp quyền từ Settings rồi quay lại app

    Trên Android việc này giết tiến trình app — app khởi động lại từ số không. Kiểm luôn: quay lại có về đúng chỗ đang dở không, xem Test app lifecycle thế nào.

  6. Chụp đủ sáu ảnh

    Lưu cùng tài liệu màn hình. Người sau không phải dựng lại sáu trạng thái này từ đầu.

Cái gì máy chặn được

Phần này nhỏ, và biết nó nhỏ là quan trọng — đừng hứa với ai rằng CI lo được nhóm này.

Chạy máy, mỗi pull request

Một luật lint hoặc một test đơn giản: cấm gọi hàm xin quyền trong màn hình khởi động. Đây là lỗi tốn kém nhất và cũng là lỗi duy nhất máy bắt chắc chắn được.

Chạy máy, mỗi đêm

UI test chạy với quyền cấp sẵn qua pm grant. Nhớ rằng đây là test luồng chính, không phải test permission — nó cố tình bỏ qua toàn bộ năm trạng thái còn lại.

Chạy người, mỗi khi màn hình đổi

Sáu bước ở trên. Dialog của hệ điều hành không tự động hoá rẻ được, và chữ trong câu giải thích là quyết định sản phẩm chứ không phải quyết định code.

Không tự động hoá

Kịch bản thu hồi quyền khi app đang chạy. Lệnh revoke giết tiến trình, nên test tự động không quan sát được app phản ứng ra sao — phần đó thuộc về nhóm lifecycle.

Giới hạn

Simulator và emulator không có người thật

Chúng dựng được trạng thái, không dựng được phản ứng. Câu giải thích viết dở thì máy vẫn báo xanh — chỉ người đọc mới thấy nó khó hiểu.

Đổi câu giải thích là đổi số liệu

Tỷ lệ deny vĩnh viễn theo từng quyền phải đo liên tục, xem Sự kiện đo lường. Không đo thì không biết câu chữ mới tốt hơn hay tệ hơn câu cũ.