AllianceProject Handbook
← Knowledge

Quy mô và hiệu năng

Kiểm thử đặc thù mobile

Bốn nhóm kịch bản chỉ mobile mới có — app lifecycle, network chaos, runtime permission và upgrade/migration. Đây là nơi sinh ra phần lớn bug lọt ra production.

Cập nhật 14/09/2026chất lượngtestmobile

8

kịch bản lifecycle

7

điều kiện mạng

6

trạng thái quyền

8

kịch bản nâng cấp

Vì sao phải tách riêng bốn nhóm này

Bốn nhóm này trả lời một câu hỏi khác với test chức năng

Test chức năng hỏi "tính năng có làm đúng việc của nó không". Bốn nhóm dưới đây hỏi "khi hệ điều hành, mạng, hoặc chính user can thiệp vào giữa chừng thì app xử lý ra sao".

App lifecycle

OS tạm dừng, đẩy xuống nền, kill bất cứ lúc nào. Tám kịch bản.

Network chaos

Mạng chậm, chập chờn, response không về. Bảy điều kiện.

Runtime permission

Sáu trạng thái, không chỉ "đã cho phép".

Upgrade và migration

User update đè, mang theo dữ liệu cũ. Tám kịch bản.

Chúng gần như không bao giờ xuất hiện khi dev tự thử trên máy mình, vì hai bên thử trong hai thế giới khác nhau:

Điều kiện dev thử

  • Máy mới, RAM dư, CPU mạnh
  • Wi-Fi văn phòng ổn định
  • Quyền đã cấp đủ từ lần chạy đầu
  • App vừa cài mới, database sạch
  • Ngồi yên một chỗ, không ai gọi đến

Điều kiện user thật

  • Máy ba năm tuổi, bộ nhớ gần đầy
  • 3G chập chờn, chuyển sang Wi-Fi giữa chừng
  • Từng bấm "Không cho phép" và quên mất
  • Update đè bản cũ, mang theo dữ liệu hai năm
  • Có cuộc gọi, có thông báo, khoá máy giữa thao tác

1. App lifecycle

Hệ điều hành có toàn quyền tạm dừng, đẩy xuống background, hoặc kill app bất cứ lúc nào. Code giả định "mình chạy liên tục từ đầu tới cuối" là code sẽ hỏng.

Hệ điều hành
App
Dữ liệu chưa gửi
  1. đẩy xuống background

  2. lưu nháp trước khi mất quyền chạy

  3. kill vì thiếu bộ nhớ

  4. user mở lại, app khởi động từ số không

  5. đọc lại nháp

  6. khôi phục đúng màn hình, không màn trắng

1.Foreground → background → foreground

State còn nguyên, không gọi lại API thừa, không mất dữ liệu đang nhập. Kịch bản này xảy ra nhiều lần mỗi phiên dùng app, nên hỏng ở đây là hỏng với mọi user.

2.App bị OS kill trong background rồi mở lại

Khôi phục đúng màn hình, hoặc về màn hình an toàn — không crash, không màn trắng. Bước "lưu nháp trước khi mất quyền chạy" là bước duy nhất app còn kiểm soát được; bỏ nó thì mọi bước sau vô nghĩa.

3.Khoá màn hình rồi mở khoá

Session còn hạn thì không bắt đăng nhập lại; hết hạn thì báo rõ.

4.Có cuộc gọi đến giữa chừng

Video hoặc audio tạm dừng đúng chỗ, quay lại tiếp tục được.

5.Push notification khi app đang mở

Không đè lên màn hình đang thao tác, deep link vào đúng chỗ.

6.Low memory

Không mất dữ liệu chưa gửi.

7.Battery saver bật

Background task bị hạn chế — app báo cho user hay im lặng hỏng?

8.Restart device

Token, cache, dữ liệu local còn dùng được.

Sáu việc phải chốt trước khi nhóm này được coi là làm xong — cả bốn nhóm dưới đây đều dùng đúng sáu ô này:

Ai chốt

Chủ kỹ thuật của module. Bốn flow rủi ro cao ở trên mỗi flow ghi tên một người trong bảng phân công, không để "team lo".

Chặn ở chặng nào

Lúc review thiết kế màn hình — màn nào giữ dữ liệu user đang nhập thì phải khai chỗ lưu nháp ngay trong thiết kế. Đợi QA thấy màn trắng là đã muộn ba chặng.

Máy hay người

Máy: unit test save/restore state chạy mỗi PR. Người: bật Don't keep activities trên Android, kill app từ recent apps rồi mở lại.

Đo bằng số nào

Crash-free session rate, tách riêng 5 giây đầu sau khi quay lại foreground. Ở 500.000 user, 99,5% nghĩa là 2.500 phiên hỏng mỗi ngày.

Bằng chứng ở đâu

Video quay màn hình kill-rồi-mở-lại, đính vào pull request, cho đúng bốn flow rủi ro cao.

Hết giờ cắt gì

Cắt battery saver và restart device trước. Không bao giờ cắt "app bị OS kill trong background" ở bốn flow đó.

Ép app vào từng trạng thái bằng lệnh gì, vì sao Don't keep activities cho kết quả xanh giả, và bảy bước kiểm một flow rủi ro cao: Test app lifecycle thế nào.

2. Network chaos

Mất mạng hoàn toàn là trường hợp dễ, vì nó rõ ràng và app dễ phát hiện. Trường hợp khó chịu hơn nhiều là mạng chậm, chập chờn, hoặc request đi được mà response không về.

App
Gateway
Máy chủ đơn hàng
  1. POST /orders kèm idempotency-key

  2. chuyển tiếp

  3. tạo đơn, trừ tiền

    tự xử lý
  4. response mất vì mạng đứt giữa chừng

  5. user bấm lại, gửi lại đúng key cũ

  6. chuyển tiếp

  7. trả lại đơn cũ, không tạo đơn thứ hai

Cùng một thao tác của user, khác nhau đúng một trường trong request:

Không có idempotency-key

  • Server thấy hai request khác nhau
  • Tạo hai đơn, trừ tiền hai lần
  • User khiếu nại, kế toán đối soát tay
  • Bug chỉ hiện ở mạng yếu nên khó tái hiện

Có idempotency-key

  • Server nhận ra key đã xử lý
  • Trả lại đúng đơn cũ, không trừ thêm
  • User bấm bao nhiêu lần cũng một kết quả
  • Test chủ động gửi trùng là chứng minh được

Các điều kiện cần giả lập:

1.Request đi nhưng response mất

Nguy hiểm nhất trong bảy điều kiện, vì app không phân biệt được với thất bại thật. Đây chính là kịch bản sơ đồ trên mô tả, và duplicate transaction là hậu quả.

2.Chuyển mạng Wi-Fi sang 4G giữa request

Socket đứt giữa chừng, tin nhắn gửi hai lần hoặc mất hẳn. Xảy ra hàng ngày với user đi ra khỏi vùng phủ Wi-Fi văn phòng.

3.Băng thông rất thấp, 100–500 Kbps

Timeout đặt quá ngắn, spinner quay vĩnh viễn.

4.Latency cao

User bấm lại nhiều lần vì tưởng máy treo.

5.Packet loss, connection reset

Retry không có backoff, đánh sập luôn server.

6.API trả 500 hoặc 503

App hiện lỗi kỹ thuật thô cho user, hoặc coi như thành công.

7.Timeout ở tầng gateway

Không phân biệt được "chưa chắc thất bại" với "chắc chắn thất bại". Hai trạng thái này cần hai cách xử lý khác nhau, và gộp chúng lại là lỗi thiết kế chứ không phải lỗi code.

Với mỗi kịch bản, câu hỏi kiểm tra luôn là năm câu này, theo đúng thứ tự:

  1. App có crash không?

  2. Spinner có quay mãi không, hay có đường thoát?

  3. Có retry vô hạn không?

  4. Có tạo ra duplicate transaction không?

    Câu quan trọng nhất với module Bán hàng.

  5. Thông báo cho user có đúng với thực tế không?

Sáu việc phải chốt trước khi nhóm này được coi là làm xong:

Ai chốt

Chủ kỹ thuật, lúc duyệt hợp đồng API. Endpoint ghi dữ liệu thì spec bắt buộc khai idempotency-key.

Chặn ở chặng nào

Vòng review thiết kế API, không phải lúc QA nhận build. POST thiếu key thì trả lại ngay từ vòng đó.

Máy hay người

Máy: test chủ động gửi trùng, chạy CI mỗi PR đụng endpoint ghi. Người: đổi Wi-Fi sang 4G giữa lúc thao tác — phần này máy giả lập không giống thật.

Đo bằng số nào

Tỷ lệ đơn trùng trên tổng đơn. Ở 500.000 user, 0,1% là 500 đơn phải đối soát tay.

Bằng chứng ở đâu

Log test gửi trùng đính vào pull request: một key, hai lần gửi, hai response giống hệt nhau.

Hết giờ cắt gì

Luồng risk thấp giữ lại ba trạng thái biên. Không bao giờ cắt idempotency ở luồng chạm tiền.

Dựng bảy điều kiện mạng đó bằng công cụ gì, cái nào đẩy được vào CI, và quy trình sáu bước chứng minh idempotency thật sự chạy: Chạy network chaos test thế nào.

3. Runtime permission

Mỗi permission phải test đủ sáu trạng thái, không chỉ trạng thái "đã cho phép":

  • Xin quyền đúng lúc user cần chức năng, kèm một câu giải thích

    • Allow

      luồng chính chạy đúng

    • Deny một lần

      app giải thích vì sao cần, cho đường thử lại

    • Deny vĩnh viễn

      app không hỏi lại vô ích, chỉ ra Settings

    • Allow once (iOS)

      lần mở sau hỏi lại, app không giả định còn quyền

    • Thu hồi từ Settings khi app đang chạy background

      quay lại app không crash

    • Bị reset sau khi update app hoặc sau thời gian dài không dùng

      coi như chưa từng cấp

Không xin permission ở màn hình đầu tiên

Xin đúng lúc user cần chức năng đó, kèm một câu giải thích. Xin sớm thì tỷ lệ deny vĩnh viễn tăng vọt, và deny vĩnh viễn thì gần như không lấy lại được.

Camera và photo

Đính kèm ảnh trong Chat.

Notification

Task nhắc hạn.

Location

Nếu có tính năng cần tới.

Sáu việc phải chốt trước khi nhóm này được coi là làm xong:

Ai chốt

Chủ sản phẩm. Xin quyền vào lúc nào và câu giải thích viết ra sao là quyết định sản phẩm, không phải quyết định code.

Chặn ở chặng nào

Lúc viết acceptance criteria cho màn hình. Sáu trạng thái ở cây trên là sáu dòng AC — thiếu dòng nào thì màn hình đó chưa đủ điều kiện giao cho dev.

Máy hay người

Gần như toàn bộ là người, vì dialog của hệ điều hành không tự động hoá rẻ được. Máy chỉ chặn được đúng một việc: lint cấm gọi xin quyền ở màn hình đầu tiên.

Đo bằng số nào

Tỷ lệ deny vĩnh viễn theo từng quyền. Notification 5% ở 500.000 user là 25.000 người không bao giờ nhận được nhắc hạn Task.

Bằng chứng ở đâu

Ảnh chụp đủ sáu trạng thái cho mỗi quyền, lưu cùng tài liệu màn hình, để người sau không phải dựng lại từ đầu.

Hết giờ cắt gì

Cắt "allow once" và "thu hồi từ Settings" ở quyền ít dùng. Không cắt deny vĩnh viễn — đó là trạng thái không lấy lại được.

Đưa quyền về từng trạng thái bằng lệnh gì, và vì sao Android không phân biệt được "chưa hỏi lần nào" với "deny vĩnh viễn": Test runtime permission thế nào.

4. Upgrade và data migration

Đây là nhóm nguy hiểm nhất, và là nhóm bị bỏ sót nhiều nhất, vì dev luôn test trên bản cài mới. User thật thì không cài mới — họ update đè lên bản cũ, mang theo dữ liệu cũ, cache cũ, token cũ, schema database cũ. Migration không chạy một bước, nó chạy nối tiếp nhau:

  1. v1.0

    bản cũ nhất còn hỗ trợ

  2. v1.4

    migration 1 → 2

  3. v2.0

    migration 2 → 3, chạy ngay sau bước trên

  4. v2.3 hiện tại

    nhảy cóc nhiều bậc là chỗ dễ gãy nhất

1.Update từ version cũ nhất còn hỗ trợ, nhảy cóc nhiều version

Migration phải chạy nối tiếp nhau, và đây là chỗ dễ gãy nhất trong tám kịch bản — mỗi bước đúng riêng lẻ không bảo đảm chuỗi bước đúng.

2.App bị kill giữa lúc migration

Lần mở sau phải chạy lại được, không để database nửa vời. Ghép với nhóm lifecycle ở trên: OS không hỏi ý kiến app trước khi kill.

3.Fresh install

Đường cơ sở.

4.Update từ version liền trước

Trường hợp phổ biến nhất.

5.Update khi đang đăng nhập

Session, token, refresh token còn dùng được.

6.Update khi token đã hết hạn

Không được rơi vào vòng lặp đăng nhập.

7.Máy gần hết dung lượng khi migration

Phải fail an toàn, không xoá dữ liệu cũ trước khi ghi xong dữ liệu mới.

8.Reinstall sau khi gỡ

Dữ liệu cũ còn sót lại có gây xung đột không.

Nguyên tắc cứng, chạy đúng ba bước và không được đảo thứ tự:

  1. Ghi xong dữ liệu mới

    Ghi sang chỗ mới, chưa đụng gì tới dữ liệu cũ.

  2. Đọc lại và xác nhận dữ liệu mới dùng được

    Không tin lệnh ghi trả về thành công là đủ.

  3. Bước này mới được xoá dữ liệu cũ

    Đảo hai bước cuối là mất dữ liệu vĩnh viễn.

Sáu việc phải chốt trước khi nhóm này được coi là làm xong:

Ai chốt

Chủ kỹ thuật, lúc duyệt thay đổi schema. Đổi schema mà không kèm kế hoạch migration thì không duyệt.

Chặn ở chặng nào

Vòng review pull request đổi schema — ba bước ghi, đọc lại, xoá phải nhìn thấy được trong diff. Sau khi phát hành thì không còn chặng nào để chặn nữa.

Máy hay người

Máy: test migration từ mọi version còn hỗ trợ, chạy trên dữ liệu mẫu thật, mỗi PR đụng schema. Người: cài bản cũ lên máy thật, dùng vài ngày cho có dữ liệu, rồi update đè.

Đo bằng số nào

Tỷ lệ mở app thất bại trong 24 giờ sau update. Ở 500.000 user, 0,2% là 1.000 người mất dữ liệu local, và không có đường rollback.

Bằng chứng ở đâu

Bảng version nguồn nhân kết quả update, lưu cùng release notes nội bộ của bản phát hành đó.

Hết giờ cắt gì

Cắt "reinstall sau khi gỡ" và "máy gần hết dung lượng". Không bao giờ cắt nhảy cóc nhiều version và kill giữa lúc migration.

Dựng dữ liệu của version cũ bằng cách nào, cài đè ra sao cho không mất dữ liệu, và cách ép hỏng giữa lúc migration mà không phải đua với hệ điều hành: Test upgrade và data migration thế nào.

Phân tầng: áp bộ nào cho luồng nào

Áp cả bốn nhóm cho mọi hạng mục thì throughput sụp. Phân theo mức risk:

Lifecycle
Network chaos
Permission
Upgrade
Cao
đầy đủ
đầy đủ
đầy đủ
đầy đủ
Trung bình
đầy đủ
đầy đủ
nếu có chạm
bản liền trước
Thấp
ba trạng thái biên: loading, rỗng, lỗi

Mức cao

Thanh toán, tạo đơn, OTP, gửi tin nhắn.

Mức trung bình

Đăng nhập, upload, đồng bộ Task.

Mức thấp

Màn hình chỉ đọc, cài đặt, trang tĩnh.

Giới hạn

Danh sách này dài

Nên nó chỉ chạy đầy đủ ở luồng mức risk cao và ở regression trước release, không chạy cho mọi pull request.