Quy mô và hiệu năng
Test upgrade và data migration thế nào
Bài kiểm thử đặc thù mobile nói phải test update đè từ mọi version còn hỗ trợ. Bài này nói dựng dữ liệu 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.
Nội dung bài
Thuật ngữ trong bài (16)
- artifact · sản phẩm dựng
- build · bản dựng
- CI · tích hợp liên tục
- commit · điểm lưu thay đổi
- pull request · yêu cầu gộp mã
- bug · lỗi
- emulator · máy giả lập
- feature flag · công tắc tính năng
- release · bản phát hành
- rollback · quay về bản cũ
- rollout · đưa bản mới ra người dùng
- low-end · máy cấu hình thấp
- cache · bộ nhớ đệm
- migration · chuyển đổi dữ liệu
- schema · hình dạng dữ liệu
- token · khóa truy cập
Kiểm thử đặc thù mobile liệt kê tám kịch bản update và ba bước bắt buộc khi migration. Bài đó dừng ở phải làm gì. Bài này là phần làm bằng cách nào — dựng dữ liệu cũ, cài đè đúng cách, và ép hỏng đúng chỗ mình muốn thay vì chờ may rủi.
Dựng dữ liệu của version cũ bằng cách nào
Không có dữ liệu cũ thì không có gì để migrate, và test sẽ xanh vì nó chẳng làm gì cả.
File dữ liệu mẫu theo từng version schema
- Mỗi lần đổi schema thì commit thêm một file database mẫu của version trước vào repo
- Test nạp file đó vào, chạy migration, kiểm kết quả
- Chạy được trong CI, tính bằng giây, không cần thiết bị
- Rẻ nhất và bền nhất — nhưng chỉ đúng nếu file mẫu được sinh từ app thật, không phải viết tay
Cài bản cũ rồi dùng tay cho có dữ liệu
- Cài APK hoặc IPA của version cũ, dùng vài ngày cho dữ liệu tự sinh ra
- Bắt được cả thứ không nằm trong database: cache, file tạm, token, thiết lập
- Không tự động hoá được, mỗi lần test lại phải dựng lại từ đầu
- Vẫn phải làm trước mỗi release, vì mục trên không phủ được phần ngoài database
Cài đè, không cài mới
adb install old.apkxcrun simctl install <device> Old.appadb install -r new.apkxcrun simctl install <device> New.app — container dữ liệu giữ nguyênadb uninstall <package> rồi cài lạixcrun simctl uninstall <device> <bundle-id> rồi cài lạiĐừng mở lại bằng nút Run của IDE
Nhiều cấu hình Run gỡ app trước khi cài. Gỡ là mất dữ liệu, và bài test mất nghĩa mà vẫn báo xanh.
Ghi lại version nguồn ở mỗi lần chạy
Bảng version nguồn nhân kết quả update là bằng chứng duy nhất chứng minh đã phủ hết, lưu cùng release notes nội bộ.
Test chuỗi migration, không test từng bước
Mỗi bước đúng riêng lẻ không bảo đảm chuỗi bước đúng. Bước 2 đổi tên cột, bước 3 đọc tên cũ — chạy riêng thì cả hai đều xanh.
Liệt kê mọi version còn hỗ trợ
Con số này là một quyết định sản phẩm, phải có người chốt. Không chốt thì mặc định là "mọi version từng phát hành", và không ai test nổi.
Với mỗi version, nạp file mẫu của chính version đó
Không nạp file mẫu của version liền trước rồi suy ra.
Chạy toàn bộ chuỗi migration tới schema hiện tại
Một lần chạy, đi qua tất cả các bậc, đúng như máy user sẽ chạy.
Kiểm dữ liệu sau khi chạy, không kiểm mã lỗi
Đếm số bản ghi, đọc vài bản ghi ra so với dữ liệu gốc. Migration trả về thành công mà mất một cột là kết quả tệ nhất, vì nó im lặng.
Chạy lại chuỗi đó lần thứ hai trên kết quả vừa xong
Phải không đổi gì. Migration chạy hai lần mà hỏng nghĩa là một lần mở app bị gián đoạn cũng hỏng.
Ép hỏng giữa lúc migration
Đừng đua với hệ điều hành. Kill đúng lúc migration đang chạy là chuyện may rủi, mà may rủi thì không lặp lại được và không đưa vào CI được.
Chèn điểm hỏng có chủ đích
Thêm một chốt trong code test cho phép ném lỗi sau bước thứ k. Chạy lần lượt k bằng 1, 2, 3… tới hết. Mỗi lần: mở lại app, kiểm migration chạy tiếp được và dữ liệu còn nguyên.
Giả lập hết dung lượng cũng làm như vậy
Ném lỗi hết chỗ ghi ở từng bước, thay vì đi đổ đầy ổ đĩa máy thật. Rẻ hơn, lặp lại được, và phủ được đúng cái cần phủ: app phải fail an toàn, không xoá dữ liệu cũ trước khi ghi xong dữ liệu mới.
Vẫn chạy tay một lần trước release
adb shell am force-stop <package> giữa lúc migration trên máy thật, vài lần, ở máy yếu nhất. Nó bắt được thứ chốt test không bắt được: hỏng ở giữa một thao tác ghi chứ không phải ở ranh giới giữa hai bước.
Đổ đầy ổ đĩa khi thật sự cần
adb shell dd if=/dev/zero of=/data/local/tmp/fill bs=1M tới khi gần đầy, nhớ xoá sau khi xong. Chỉ làm khi nghi ngờ chính hệ thống file, không làm như thói quen.
Cái gì đẩy được vào CI
Chạy máy, mỗi pull request đụng schema
Chuỗi migration từ mọi version còn hỗ trợ, trên file dữ liệu mẫu. Đây là cổng chặn — pull request đổi schema mà không có test này thì không duyệt.
Chạy máy, cùng vòng đó
Vòng lặp ném lỗi sau bước thứ k. Số bước ít nên rẻ, và nó là thứ duy nhất chứng minh migration chạy lại được.
Chạy người, trước mỗi release
Cài bản cũ lên máy thật, dùng vài ngày, update đè. Ít nhất hai version nguồn: version liền trước và version cũ nhất còn hỗ trợ.
Không đẩy vào CI
Reinstall sau khi gỡ, và máy gần hết dung lượng thật. Để trong checklist release.
Giới hạn
Không có đường lùi
Trên mobile không rollback được binary đã cài, càng không rollback được dữ liệu đã hỏng. Ở 500.000 user, tỷ lệ mở app thất bại 0,2% trong 24 giờ sau update là 1.000 người mất dữ liệu local — không có nút nào bấm để trả lại.
Nên phát hành từ từ
Bản có migration phải đi qua rollout theo phần trăm và có sẵn tiêu chí dừng, xem Phát hành từ từ và feature flag.

