Mở đầu — vì sao bài này quan trọng
Có một sự thật phũ phàng trong nghề QA di động mà ít ai nói thẳng với người mới: một con bug bạn tìm ra chỉ thực sự "có giá trị" khi lập trình viên đọc report của bạn xong là họ tái hiện được, hiểu được, và sửa được — mà không phải quay lại hỏi bạn nửa câu nào. Bạn có thể là người test giỏi nhất công ty, phát hiện ra lỗi crash hiếm gặp mà 10.000 người dùng vẫn chưa ai gặp, nhưng nếu bug report của bạn viết kiểu "app bị lỗi khi bấm nút, dev xem giúp em nhé" thì công sức đó gần như bằng không.
Trên mobile, việc viết bug report còn khó hơn web gấp nhiều lần. Web bạn chỉ cần lo về trình duyệt và độ phân giải. Mobile thì có cả một ma trận: iOS hay Android, phiên bản OS nào, hãng máy gì (Samsung, Xiaomi, Oppo hay iPhone), build number bao nhiêu, còn bao nhiêu RAM, mạng 4G hay WiFi, pin còn bao nhiêu phần trăm, và thậm chí bug chỉ xảy ra khi máy đang nóng. Một bug "không tái hiện được" trên máy dev nhưng lại chết trên máy người dùng thật ở Việt Nam là chuyện thường ngày ở huyện.
Bài này sẽ dạy bạn cách viết bug report cho mobile theo chuẩn công nghiệp: đủ thông tin để dev không phải hỏi lại, đủ ngắn gọn để họ đọc trong 30 giây, và đủ thuyết phục để lỗi được ưu tiên sửa. Đây là kỹ năng "mềm" nhưng quyết định trực tiếp bạn được xem là một QA đáng tin cậy hay chỉ là người "bấm bấm app rồi báo lỗi".
Khái niệm cốt lõi
Một bug report tốt trả lời được 5 câu hỏi
Trước khi bàn template, hãy nhớ bản chất: bug report là một cách giao tiếp. Người đọc (dev, PM, hoặc chính bạn 3 tháng sau) cần biết:
- Cái gì sai? (Actual result — điều đang xảy ra)
- Đáng lẽ phải thế nào? (Expected result — điều lẽ ra phải xảy ra)
- Làm sao để thấy lại? (Steps to reproduce)
- Xảy ra ở đâu? (Environment — thiết bị, OS, build)
- Nghiêm trọng thế nào? (Severity & Priority)
Severity và Priority không phải là một
Đây là chỗ nhiều bạn mới nhầm. Severity (mức độ nghiêm trọng về mặt kỹ thuật) do QA quyết định, còn Priority (thứ tự ưu tiên sửa về mặt kinh doanh) do PM/team quyết định.
- App crash khi mở màn hình Cài đặt mà 0,1% người dùng vào → Severity cao (Critical, vì crash) nhưng Priority có thể thấp (ít người gặp).
- Nút "Thanh toán" bị lệch 2px trên iPhone 15 Pro Max → Severity thấp (chỉ là giao diện) nhưng Priority có thể cao (ảnh hưởng trực tiếp doanh thu, ai cũng thấy).
- Critical / Blocker: app crash, mất dữ liệu, không thể đăng nhập, không thanh toán được, ANR (Android Not Responding).
- High / Major: chức năng chính hỏng nhưng có đường vòng, sai dữ liệu hiển thị.
- Medium: chức năng phụ lỗi, UI vỡ nhưng dùng được.
- Low / Trivial: sai chính tả, lệch pixel, màu sắc chưa đúng design.
Những trường thông tin ĐẶC THÙ của mobile
Ngoài Title/Steps/Expected/Actual quen thuộc, bug report mobile bắt buộc phải có những trường mà web không cần:
- Thiết bị & model cụ thể: "Samsung A35" chứ không phải "điện thoại Android". Bug có thể chỉ xảy ra trên chip MediaTek hoặc màn hình có tai thỏ.
- Phiên bản OS: Android 14, iOS 17.4. Bug quyền (permission) hay đổi hành vi theo từng bản OS.
- Build number / version app: "2.3.1 (build 452)". Không có nó, dev không biết bug thuộc code nào.
- Loại kết nối mạng: WiFi, 4G, hay Airplane mode. Nhiều bug chỉ chết ở mạng yếu.
- Network log / API response: với bug liên quan dữ liệu.
- Crash log / stack trace: từ Logcat (Android) hoặc Console/Crashlytics (iOS).
- Screenshot có khoanh vùng hoặc video quay màn hình: mobile hầu như luôn cần trực quan.
- Điều kiện đặc biệt: máy đang nóng, pin thấp, vừa xoay ngang màn hình, vừa nhận cuộc gọi đến.
Tình huống thực tế
Tình huống 1 — Bug report "vô dụng" khiến team fintech mất 3 ngày
Một startup ví điện tử ở TP.HCM (tạm gọi PayViet) có bạn QA mới báo một bug: "App crash ở màn hình đăng nhập, dev fix gấp giúp em." Dev cầm iPhone của mình test đi test lại cả buổi sáng — không crash. Bug bị đóng với trạng thái "Cannot reproduce". Ba ngày sau, số lượng crash trên Crashlytics tăng vọt, hàng trăm người dùng không đăng nhập được.
Điều tra lại mới phát hiện: bug chỉ xảy ra khi người dùng paste email từ clipboard có chứa khoảng trắng ở cuối, và chỉ trên Android 14 của Samsung do bàn phím Samsung tự thêm ký tự đặc biệt. Bạn QA có gặp bug nhưng không ghi rõ máy gì, không ghi thao tác "paste", nên dev không thể tái hiện.
Bài học: "App crash ở màn hình đăng nhập" là mô tả vô nghĩa nếu thiếu ngữ cảnh. Nếu report ghi rõ "Samsung A35, Android 14, thao tác paste email từ clipboard" thì dev đã sửa trong 30 phút thay vì để lỗi trôi ra production 3 ngày. Con số thực tế: 3 ngày downtime của luồng đăng nhập ở một ví điện tử có thể mất hàng nghìn giao dịch.
Tình huống 2 — Video quay màn hình cứu một bug "ma"
Ở một công ty thương mại điện tử tại Hà Nội, QA phát hiện giỏ hàng thỉnh thoảng bị xóa sạch sản phẩm mà không rõ lý do. Bug này cực khó tái hiện — có khi 20 lần mới dính 1 lần. Nếu chỉ viết chữ, không ai tin và bug sẽ bị deprioritize.
Bạn QA quyết định bật tính năng quay màn hình liên tục (screen recording) trong suốt buổi test, kèm bật hiển thị thao tác chạm (Developer Options → Show taps trên Android). Đến lần thứ 18 thì bug xảy ra, và video ghi lại chính xác: lỗi chỉ xuất hiện khi người dùng đang ở màn hình giỏ hàng thì có cuộc gọi đến, app bị đưa xuống background quá 30 giây, rồi mở lại — session bị hết hạn và giỏ hàng bị reset thay vì được lưu lại.
Nhờ video và mô tả điều kiện "background hơn 30 giây", dev tìm ra ngay vấn đề ở logic khôi phục session. Bug được nâng Priority lên High vì ảnh hưởng trực tiếp tỷ lệ chuyển đổi đơn hàng.
Bài học: với bug khó tái hiện, một video 20 giây đáng giá hơn 200 chữ mô tả. Và luôn ghi lại điều kiện môi trường bất thường (background, cuộc gọi đến, xoay màn hình) vì đó thường là thủ phạm của các bug "ma" trên mobile.
Tình huống 3 — Log Logcat biến "không tái hiện được" thành "sửa trong 1 giờ"
Một team làm app giao đồ ăn báo lỗi: màn hình bản đồ thỉnh thoảng trắng xóa khi tài xế đang giao hàng. QA gắn máy Android vào máy tính, chạy adb logcat và lọc theo tên package của app. Khi bug xảy ra, log hiện dòng OutOfMemoryError do app load quá nhiều ảnh marker bản đồ mà không giải phóng bộ nhớ.
QA đính kèm đoạn log này vào bug report, ghi rõ dòng exception. Dev không cần tái hiện — chỉ đọc stack trace là biết chính xác dòng code nào rò rỉ bộ nhớ.
Bài học: crash log/stack trace là "bằng chứng vàng". Với QA mobile, biết dùng adb logcat (Android) và đọc Console log/Crashlytics (iOS) là kỹ năng nâng bạn từ "người báo lỗi" lên "người đưa ra manh mối kỹ thuật". Một report có stack trace thường được sửa nhanh gấp 5–10 lần report chỉ có chữ.
Hướng dẫn từng bước
Đây là quy trình chuẩn để viết một bug report mobile chất lượng, từ lúc bạn vừa gặp lỗi:
Bước 1 — Dừng lại và tái hiện lại ít nhất một lần. Đừng vội báo ngay. Hãy làm lại đúng các bước để chắc chắn lỗi không phải do bạn thao tác nhầm. Ghi lại chính xác bạn đã bấm gì.
Bước 2 — Chụp/quay bằng chứng NGAY khi lỗi còn trên màn hình. Screenshot màn hình lỗi, hoặc quay video nếu là lỗi động (animation, crash, luồng nhiều bước). Nếu là crash, lấy log ngay (adb logcat hoặc mở Crashlytics).
Bước 3 — Ghi lại thông tin môi trường. Vào Cài đặt để lấy chính xác model máy, phiên bản OS, và vào phần About của app để lấy version + build number.
Bước 4 — Viết Title theo công thức [Khu vực] + Hành động + Kết quả. Ví dụ: [Đăng nhập] App crash khi paste email từ clipboard. Title phải đọc là hiểu bug ngay, không cần mở chi tiết.
Bước 5 — Điền template chuẩn. Dưới đây là mẫu bạn nên dùng và tùy biến theo công cụ (Jira, Trello, hay bảng Excel):
Title: [Login] App crash khi paste email từ clipboard
Severity: High
Priority: High
Environment:
- Device: Samsung Galaxy A35 5G
- OS: Android 14 (One UI 6.1)
- App version: 2.3.1 (build 452)
- Network: WiFi
- Build type: Staging
Preconditions:
- Đã có email dài kèm khoảng trắng cuối trong clipboard
Steps to reproduce:
1. Mở app, vào màn hình Đăng nhập
2. Nhấn giữ vào ô Email → chọn Paste
3. Nhập mật khẩu bất kỳ
4. Bấm nút "Đăng nhập"
Expected result:
- App gọi API đăng nhập, tự trim khoảng trắng thừa
Actual result:
- App crash ngay khi paste (100% tái hiện, 5/5 lần)
Attachments:
- screen-recording.mp4
- logcat-crash.txt (NullPointerException dòng 214)
Notes:
- Chỉ xảy ra trên bàn phím Samsung, không dính trên Gboard
Bước 6 — Gán Severity và ghi tỷ lệ tái hiện. Ghi rõ "5/5 lần" hay "1/20 lần" — dev cần biết bug này chắc chắn hay ngẫu nhiên.
Bước 7 — Đọc lại như thể bạn là dev. Tự hỏi: "Nếu tôi chưa từng biết app này, đọc report này tôi có tái hiện được không?" Nếu còn phải đoán, bổ sung thêm.
Lỗi thường gặp & mẹo
Lỗi 1 — Title mơ hồ. "App bị lỗi", "Không hoạt động", "Có vấn đề" là những title vô dụng. Luôn dùng công thức [Khu vực] + hành động + kết quả.
Lỗi 2 — Gộp nhiều bug vào một report. Mỗi bug một ticket. Nếu bạn báo "màn hình Cài đặt có 5 lỗi" trong một ticket, dev sửa được 3 lỗi thì không biết đóng hay để mở, và việc theo dõi trở thành ác mộng.
Lỗi 3 — Quên build number. Đây là lỗi kinh điển. Không có build number, dev không biết bug thuộc bản build nào, có thể đã sửa ở build mới rồi mà bạn test bản cũ.
Lỗi 4 — Screenshot chụp cả màn hình mà không khoanh vùng. Hãy dùng công cụ đánh dấu (mũi tên, khung đỏ) để chỉ đúng chỗ lỗi. Dev không có thời gian tự tìm.
Lỗi 5 — Không ghi tỷ lệ tái hiện. Bug ngẫu nhiên (flaky) mà bạn báo như bug chắc chắn 100% sẽ khiến dev mất niềm tin khi họ không tái hiện được.
Lỗi 6 — Viết Expected và Actual giống hệt nhau hoặc bỏ trống Expected. Đôi khi cái bạn cho là "lỗi" thực ra là đúng thiết kế. Ghi rõ Expected buộc bạn tự kiểm tra lại và giúp dev/PM tranh luận đúng trọng tâm.
Mẹo hay:
- Bật Show taps / Pointer location trong Developer Options khi quay video để dev thấy bạn chạm vào đâu.
- Với iOS, dùng TestFlight feedback cho phép người test chụp màn hình kèm gửi log tự động — tận dụng nó.
- Tạo sẵn một snippet template trong công cụ ghi chú để mỗi lần báo bug chỉ việc điền, không quên trường nào.
- Với bug về dữ liệu, luôn kèm API request/response (qua Charles Proxy hoặc log). Một response sai từ backend giúp dev khoanh vùng ngay là lỗi client hay server.
- Ghi thời điểm chính xác khi crash xảy ra (giờ:phút) — dev có thể đối chiếu với log server hoặc Crashlytics.
Bài tập thực hành
- Viết lại từ report tệ. Lấy report sau và viết lại cho chuẩn: "App bị đơ khi tôi mua hàng, sửa giúp nhé." Hãy bổ sung đầy đủ Title, Environment, Steps, Expected, Actual, Severity, Priority với giả định hợp lý (tự chọn thiết bị Việt Nam như Oppo Reno, Xiaomi Redmi).
- Phân loại Severity vs Priority. Cho 5 bug sau, gán Severity và Priority kèm giải thích: (a) app crash khi mở app trên iPhone SE; (b) logo bị mờ trên màn hình splash; (c) không thanh toán được bằng MoMo; (d) sai chính tả "Đăng nhâp" ở nút chính; (e) danh sách sản phẩm load chậm 8 giây trên 4G.
- Thực hành lấy log. Cắm một máy Android vào máy tính, chạy
adb logcatvà lọc theo package name của một app bất kỳ. Gây ra một hành động (mở màn hình nào đó) và tìm dòng log tương ứng. Chụp lại và tập đọc để nhận diện đâu là exception.
- Tạo template cá nhân. Soạn một bug report template hoàn chỉnh cho riêng bạn (dạng snippet) có đủ tất cả các trường mobile đặc thù đã học, sẵn sàng copy-paste khi làm việc thật.
Tóm tắt
Bug report cho mobile không phải là "báo lỗi cho có" — đó là sản phẩm giao tiếp quyết định giá trị công việc QA của bạn. Một report tốt trả lời được năm câu hỏi: cái gì sai, đáng lẽ thế nào, tái hiện ra sao, xảy ra ở đâu, và nghiêm trọng đến mức nào.
Điều làm bug report mobile khác biệt so với web là những trường đặc thù bắt buộc: thiết bị và model cụ thể, phiên bản OS, build number, loại kết nối mạng, điều kiện môi trường bất thường (background, cuộc gọi đến, máy nóng), và đặc biệt là bằng chứng trực quan (screenshot có khoanh vùng, video quay màn hình) cùng crash log/stack trace.
Ba tình huống thực tế cho thấy: một report thiếu ngữ cảnh khiến team fintech mất 3 ngày downtime; một video 20 giây cứu được bug "ma" giỏ hàng; và một đoạn Logcat biến "không tái hiện được" thành "sửa trong 1 giờ". Hãy nhớ: đầu tư 5 phút viết report tử tế tiết kiệm cho cả team hàng giờ, có khi hàng ngày. Đó là điều tách biệt một QA đáng tin cậy khỏi phần còn lại.