Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa test xong một tính năng đặt hàng trên văn phòng có Wi-Fi cáp quang 200 Mbps. Mọi thứ mượt như bơ: bấm nút là màn hình chuyển ngay, ảnh sản phẩm hiện tức thì, thanh toán xong trong một giây. Bạn tự tin ký duyệt cho release. Rồi ba ngày sau, bộ phận chăm sóc khách hàng ngập trong khiếu nại: khách bấm "Đặt hàng" hai lần vì lần đầu tưởng bị treo, dẫn đến hai đơn trùng; ảnh sản phẩm không load nên khách không biết mình mua gì; có người bị trừ tiền nhưng đơn không được ghi nhận.
Chuyện gì đã xảy ra? Rất đơn giản: khách hàng của bạn không ngồi trong văn phòng có cáp quang. Họ đang đứng ở chợ Bến Thành với 2 vạch sóng 3G, đang đi xe bus qua hầm Thủ Thiêm mất sóng vài giây, hay đang ở một huyện miền núi nơi mạng chập chờn cả ngày. Ở Việt Nam và cả Đông Nam Á, một tỷ lệ rất lớn người dùng truy cập ứng dụng qua mạng di động chất lượng không ổn định. Nếu bạn chỉ test trên mạng tốt, bạn đang bỏ sót đúng điều kiện mà đa số người dùng thật đang trải nghiệm.
Bài này dạy bạn cách chủ động giả lập mạng yếu (network throttling) và mất mạng (offline) để phát hiện các lỗi mà mạng tốt vĩnh viễn che giấu: race condition khi request chậm, spinner quay vô tận, gửi trùng đơn, mất dữ liệu khi rớt mạng giữa chừng. Đây là kỹ năng phân biệt một tester "bấm cho chạy được" với một tester thật sự hiểu hành vi người dùng.
Khái niệm cốt lõi
Mạng yếu gồm những chiều nào?
Nhiều người nghĩ "mạng yếu" chỉ là "chậm", nhưng thực ra nó có bốn chiều độc lập, và mỗi chiều gây ra một loại lỗi khác nhau:
- Bandwidth (băng thông) — lượng dữ liệu tải được mỗi giây, đo bằng Mbps/Kbps. Băng thông thấp làm ảnh, video, file lớn tải lâu.
- Latency / RTT (độ trễ) — thời gian một gói tin đi và về (Round Trip Time), đo bằng mili-giây (ms). Đây là "kẻ giết người" thật sự với ứng dụng có nhiều request nhỏ: dù băng thông rộng, mỗi request vẫn phải chờ RTT. Một trang gọi 30 API tuần tự với RTT 600ms sẽ chậm khủng khiếp dù bạn có tải cả gigabyte cũng nhanh.
- Packet loss (mất gói) — tỷ lệ gói tin bị rớt dọc đường. Mạng di động khi di chuyển thường mất gói, gây timeout ngẫu nhiên và request "treo lơ lửng".
- Kết nối chập chờn (intermittent / flaky) — mạng lúc có lúc không, chuyển qua lại giữa Wi-Fi và 4G, đi qua vùng lõm sóng. Đây là kịch bản thực tế nhất và cũng khó test nhất.
Các profile chuẩn để giả lập
Chrome DevTools và hầu hết công cụ đều dùng vài preset. Ghi nhớ con số này để nói chuyện cho chuẩn:
| Profile | Download | Upload | Latency (RTT) |
|---|---|---|---|
| Fast 3G | ~1.5 Mbps | ~750 Kbps | ~562 ms |
| Slow 3G | ~400 Kbps | ~400 Kbps | ~2000 ms |
| Offline | — |
Offline-first là gì?
Offline-first là triết lý thiết kế ứng dụng coi mất mạng là trạng thái bình thường, không phải ngoại lệ. Ứng dụng offline-first sẽ:
- Lưu dữ liệu vào bộ nhớ cục bộ (localStorage, IndexedDB, cache) để hiển thị ngay cả khi không có mạng.
- Cho phép người dùng tiếp tục thao tác (soạn tin, thêm giỏ hàng) rồi đồng bộ (sync) lên server khi có mạng trở lại.
- Dùng Service Worker (với web) để phục vụ nội dung từ cache và quản lý hàng đợi request.
Ba trạng thái mạng bạn phải luôn kiểm tra
Với mỗi luồng quan trọng, hãy nghĩ theo ba pha:
- Trong khi tải chậm — có hiện loading/skeleton không? Có disable nút để chặn double-submit không? Người dùng có biết hệ thống đang xử lý không?
- Khi mất mạng hoàn toàn — thông báo có rõ ràng không? Dữ liệu đang nhập có bị mất không? Có nút "Thử lại" không?
- Khi mạng phục hồi — ứng dụng có tự đồng bộ / tự retry không, hay bắt người dùng F5 thủ công? Có tạo ra dữ liệu trùng khi retry không?
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT và đơn hàng trùng ở Slow 3G
Một sàn thương mại điện tử tầm trung tại TP.HCM (tạm gọi "ChợNhanh") có nút "Đặt hàng" gọi API mất trung bình 4-6 giây khi ở Slow 3G. Vấn đề: nút không bị disable sau khi bấm, và không có spinner rõ ràng. Người dùng ở vùng sóng yếu bấm nút, thấy màn hình "đứng im" 5 giây, tưởng lỗi nên bấm lại. Kết quả: hệ thống nhận hai request tạo đơn, tạo ra hai đơn giống hệt.
Đội QA phát hiện ra khi một bạn tester bật Slow 3G trong DevTools, mở tab Network, bấm "Đặt hàng" rồi bấm thêm lần nữa trong lúc request đầu còn "pending". Tab Network hiện rõ hai request POST /orders màu cam đang chạy song song. Trong tháng đó, đội vận hành ước tính khoảng 3% đơn ở khu vực tỉnh là đơn trùng, gây tốn chi phí xử lý hoàn/hủy.
Bài học: Double-submit chỉ lộ ra khi request đủ chậm. Trên mạng tốt request xong trong 300ms nên gần như không ai kịp bấm hai lần. Muốn bắt lỗi này, bạn bắt buộc phải throttle mạng. Cách sửa: disable nút ngay khi bấm + hiện spinner + server dùng idempotency key để chặn tạo trùng.
Ví dụ 2 — App tin nhắn và tin "bốc hơi" khi qua hầm
Một startup làm ứng dụng nhắn tin nội bộ cho doanh nghiệp (tạm gọi "TalkVN") quảng cáo là "hoạt động cả khi offline". QA kiểm tra kịch bản thực tế: người dùng đang đi xe qua hầm Hải Vân, soạn một tin nhắn dài, bấm gửi đúng lúc mất sóng. Trên bản build đầu tiên, tin nhắn hiện trạng thái "đang gửi" rồi... biến mất luôn khi có sóng lại, vì client không lưu tin vào hàng đợi cục bộ mà chỉ giữ trong bộ nhớ tạm, và khi mạng phục hồi thì màn hình đã bị re-render.
Tester tái hiện bằng cách: gõ tin, chuyển DevTools sang Offline, bấm gửi, đợi tin ở trạng thái "sending", rồi chuyển lại Online. Đáng lẽ tin phải tự gửi đi; thực tế nó mất. Đây đúng là lỗi phá vỡ lời hứa offline-first.
Bài học: Với app offline-first, kịch bản "mất mạng giữa lúc thao tác rồi có mạng lại" là ca test số một, không phải ca phụ. Phải kiểm tra dữ liệu được lưu bền vững (persist) và tự đồng bộ khi online, không được để mất.
Ví dụ 3 — Ví điện tử và màn hình trắng khi ảnh không load
Một ví điện tử ở Đông Nam Á có màn hình xác nhận thanh toán hiển thị logo ngân hàng và QR bằng ảnh tải từ CDN. Ở vùng mạng yếu, các ảnh này tải rất lâu. Bản đầu tiên bố trí layout phụ thuộc kích thước ảnh, nên khi ảnh chưa về, cả khối nội dung bị co lại thành một dải trắng, người dùng không thấy nút "Xác nhận" ở đâu và tưởng app hỏng.
QA bắt được lỗi này bằng Fast 3G: mở màn hình thanh toán, quan sát trong vài giây đầu khi ảnh chưa load. Họ chụp lại trạng thái "khoảng trắng" và đề xuất dùng placeholder có kích thước cố định + nút "Xác nhận" render độc lập không chờ ảnh.
Bài học: Đừng chỉ chờ đến khi trang load xong mới đánh giá. Chính trạng thái trung gian (lúc đang tải) mới là nơi mạng yếu phơi bày lỗi UI/UX. Hãy xem kỹ giai đoạn "đang tải" chứ không phải chỉ kết quả cuối.
Hướng dẫn từng bước
Bước 1 — Giả lập mạng chậm bằng Chrome DevTools
Đây là công cụ miễn phí, luôn có sẵn, dùng cho web:
- Mở trang web cần test, nhấn F12 để mở DevTools.
- Vào tab Network.
- Tìm dropdown Throttling (mặc định ghi "No throttling") trên thanh công cụ.
- Chọn Slow 3G hoặc Fast 3G. Từ đây mọi request sẽ bị làm chậm theo profile đã chọn.
- Tick ô Disable cache để mô phỏng người dùng vào lần đầu (không có cache sẵn) — đây là điều kiện khắc nghiệt và trung thực hơn.
- Bấm Reload và quan sát toàn bộ hành trình tải trang.
Bước 2 — Test trạng thái Offline và phục hồi
- Trong dropdown Throttling, chọn Offline.
- Thực hiện các thao tác quan trọng: điều hướng trang, gửi form, thêm giỏ hàng. Ghi nhận app phản ứng thế nào — có báo lỗi rõ ràng không, có giữ dữ liệu không.
- Chuyển lại No throttling / Online.
- Quan sát: app có tự đồng bộ / tự retry không? Có tạo dữ liệu trùng không? Người dùng có phải F5 thủ công không?
Bước 3 — Test trên thiết bị và mạng thật
DevTools giả lập tốt nhưng không thay được thực tế. Bổ sung thêm:
- Điện thoại thật + bật/tắt chế độ máy bay giữa lúc thao tác để tạo mất mạng đột ngột.
- Trên Android, dùng tùy chọn nhà phát triển hoặc app giả lập mạng; trên iOS có Network Link Conditioner (trong Developer settings) để throttle ở cấp hệ điều hành, áp dụng cho cả app native.
- Test thật ở vùng sóng yếu nếu có thể (tầng hầm, thang máy, khu vực xa trung tâm).
Bước 4 — Ghi lại bằng chứng và viết bug chuẩn
Khi phát hiện lỗi, bug report cần ghi rõ:
- Profile mạng cụ thể đã dùng (ví dụ "Slow 3G, RTT ~2000ms, Disable cache: ON").
- Các bước tái hiện kèm thời điểm chuyển trạng thái mạng.
- Ảnh chụp tab Network cho thấy request pending/failed, và video quay màn hình nếu là lỗi timing.
Lỗi thường gặp & mẹo
Lỗi 1: Chỉ test trên mạng tốt của công ty. Đây là cái bẫy phổ biến nhất. Hãy đưa "test tối thiểu ở Slow 3G và Offline" thành checklist bắt buộc cho mọi luồng quan trọng (đăng nhập, thanh toán, gửi form, upload).
Lỗi 2: Quên tick Disable cache. Lần test thứ hai trở đi, cache làm trang tải nhanh giả tạo, che mất trải nghiệm của người dùng mới. Luôn bật Disable cache khi đo mạng yếu.
Lỗi 3: Bỏ qua trạng thái phục hồi mạng. Nhiều tester test được lúc offline rồi dừng, mà quên kiểm tra khi online lại. Chính lúc phục hồi mới hay sinh lỗi trùng dữ liệu hoặc kẹt spinner. Luôn test đủ chu trình offline → online.
Lỗi 4: Không kiểm tra double-submit và timeout. Trên mạng chậm, hãy cố tình bấm nút nhiều lần, hãy để request chạy tới lúc timeout. Kiểm tra app có disable nút, có thông báo timeout thân thiện, và có nút "Thử lại" không.
Mẹo — nghĩ theo trục latency, không chỉ bandwidth. Nếu app của bạn gọi nhiều API nhỏ, hãy tạo profile RTT cao để bắt lỗi độ trễ. Đây là thứ mạng tốt che giấu triệt để nhất.
Mẹo — throttle cả upload. Người Việt hay upload ảnh CCCD, ảnh sản phẩm, video. Băng thông upload luôn thấp hơn download nhiều, nên upload là điểm dễ vỡ. Test upload file lớn ở Slow 3G để xem có progress bar, có hủy được, có timeout gọn gàng không.
Mẹo — kiểm tra thông báo lỗi có "người" không. Đừng chấp nhận màn hình trắng hay lỗi kỹ thuật "NetworkError: Failed to fetch". Người dùng cần câu như "Mạng đang yếu, vui lòng thử lại" kèm nút hành động.
Bài tập thực hành
Hãy chọn một website hoặc app bạn đang test (hoặc một trang TMĐT bất kỳ) và làm các bài sau, ghi kết quả vào một bảng.
- Đo baseline vs Slow 3G. Mở DevTools, tab Network, Disable cache ON. Reload trang chủ ở "No throttling" rồi ở "Slow 3G". Ghi lại thời gian load và mô tả sự khác biệt về trải nghiệm (có skeleton không, khoảng trắng ở đâu).
- Săn double-submit. Bật Slow 3G, tìm một nút submit quan trọng (gửi form, đặt hàng). Bấm nhanh hai lần liên tiếp khi request đầu còn pending. Kiểm tra trong tab Network có hai request không. Kết luận app có chặn double-submit không.
- Chu trình offline → online. Nhập dữ liệu vào một form, chuyển sang Offline, bấm gửi, quan sát thông báo và xem dữ liệu có bị mất không. Sau đó chuyển lại Online và ghi nhận app có tự đồng bộ/retry hay bắt bạn F5.
- Viết một bug report hoàn chỉnh cho bất kỳ lỗi nào bạn tìm được ở ba bài trên, ghi rõ profile mạng, các bước tái hiện có mốc chuyển trạng thái mạng, kết quả mong đợi và thực tế, kèm ảnh chụp tab Network.
Tóm tắt
- Đa số người dùng Việt Nam và Đông Nam Á truy cập qua mạng di động chất lượng không ổn định, nên test mạng yếu không phải tùy chọn mà là bắt buộc.
- "Mạng yếu" có bốn chiều độc lập: bandwidth, latency (RTT), packet loss, và kết nối chập chờn — mỗi chiều lộ ra một loại lỗi khác nhau; latency thường bị đánh giá thấp nhất.
- Ghi nhớ mốc chuẩn: Fast 3G (~1.5 Mbps, RTT ~562ms) và Slow 3G (~400 Kbps, RTT ~2000ms); dùng Slow 3G làm mức test tối thiểu.
- Luôn kiểm tra đủ ba trạng thái: đang tải chậm, mất mạng hoàn toàn, và mạng phục hồi. Kịch bản vàng là Online → nhập liệu → Offline → gửi → Online.
- Công cụ chính: Chrome DevTools Network Throttling (nhớ tick Disable cache), bổ sung bằng thiết bị thật + chế độ máy bay và Network Link Conditioner cho app native.
- Các lỗi kinh điển mạng yếu phơi bày: double-submit tạo đơn trùng, spinner treo vô tận, mất dữ liệu khi rớt mạng, màn hình trắng khi ảnh chưa load, và thông báo lỗi kỹ thuật khó hiểu.
- Khi viết bug, luôn ghi rõ profile mạng cụ thể để dev tái hiện được.