Mở đầu — vì sao bài này quan trọng
Nếu bạn từng ngồi trong một buổi họp QA của team Việt Nam và nghe hai người tranh cãi kiểu "Cái này là smoke test hay sanity test vậy?", thì bạn không đơn độc. Đây là một trong những cặp thuật ngữ bị nhầm lẫn nhiều nhất trong nghề kiểm thử, không chỉ ở Việt Nam mà trên toàn thế giới. Vấn đề là hai khái niệm này nghe na ná nhau — cả hai đều là bài kiểm tra nhanh, đều chạy trước khi kiểm thử sâu, và đều dùng để "gác cổng". Nhưng chúng phục vụ hai mục đích hoàn toàn khác nhau, ở hai thời điểm khác nhau, với hai tâm thế khác nhau.
Hiểu sai cặp này không chỉ là chuyện thuật ngữ cho vui. Nó dẫn đến những hậu quả rất thật: bạn dành nửa ngày regression một build mà lẽ ra nên "reject" ngay từ đầu vì nó còn không mở nổi trang login; hoặc bạn báo cáo với sếp "đã sanity xong, ổn rồi" trong khi thực chất bạn mới chỉ smoke qua loa. Khi giao tiếp với dev, PM, hay khi viết test report, dùng đúng từ giúp cả team hiểu chính xác mức độ tin cậy của một build.
Trong bài này, mình sẽ giúp bạn phân biệt rạch ròi smoke test và sanity test — không phải bằng định nghĩa học thuộc lòng, mà bằng cách hiểu bản chất "khi nào dùng cái nào và tại sao". Sau bài này, bạn sẽ tự tin trả lời được câu hỏi kinh điển đó, và quan trọng hơn, biết cách áp dụng đúng vào công việc hằng ngày.
Khái niệm cốt lõi
Smoke Test — kiểm tra "còn thở không"
Thuật ngữ "smoke test" bắt nguồn từ ngành phần cứng điện tử. Khi kỹ sư lắp xong một bảng mạch mới, việc đầu tiên họ làm là cắm điện. Nếu bảng mạch bốc khói (smoke) thì rõ ràng hỏng nặng, khỏi cần test tiếp làm gì. Ý tưởng đó được mượn sang phần mềm: sau mỗi build mới, ta chạy một loạt kiểm tra nhanh trên các chức năng CHÍNH để xác nhận build này "còn thở", đủ ổn định để đưa vào test sâu hơn.
Đặc điểm cốt lõi của smoke test:
- Rộng và nông (wide and shallow): nó chạm vào nhiều chức năng chính của hệ thống nhưng chỉ ở mức bề mặt. Kiểu như đi một vòng kiểm tra "login được không, thêm sản phẩm vào giỏ được không, thanh toán ra được màn hình xác nhận không" — chứ không đi sâu kiểm tra từng trường hợp giá tiền, mã giảm giá, hay validation.
- Chạy sau mỗi build mới: đây là bước "gác cổng" đầu tiên. Build vừa được deploy lên môi trường test? Chạy smoke trước đã.
- Mục đích chính: xác định build có ĐỦ TỐT để tiếp tục kiểm thử hay không. Nếu smoke fail, ta reject build ngay, trả lại cho dev, tiết kiệm thời gian cho cả team.
- Thường được lên kịch bản trước (scripted): vì chạy lặp đi lặp lại với mỗi build, smoke test rất dễ và rất nên được tự động hóa.
- Còn gọi là "Build Verification Test" (BVT): nhiều công ty dùng tên này để nhấn mạnh vai trò xác minh build.
Sanity Test — kiểm tra "có còn tỉnh táo không"
"Sanity" nghĩa là sự tỉnh táo, lành mạnh. Sanity test là bài kiểm tra nhanh để xác nhận rằng một thay đổi cụ thể (một bug fix, một tính năng nhỏ vừa sửa) đã hoạt động đúng, và cái sửa đó không phá vỡ những phần liên quan trực tiếp xung quanh nó. Nó giống như bạn hỏi: "Cái sửa này có hợp lý không, có 'tỉnh táo' không, đáng để mình bỏ công test sâu tiếp không?"
Đặc điểm cốt lõi của sanity test:
- Hẹp và sâu (narrow and deep): nó tập trung vào MỘT hoặc vài chức năng cụ thể vừa được sửa, nhưng đi sâu vào chi tiết của chức năng đó. Ví dụ: dev vừa fix bug tính sai tiền ship, thì sanity test sẽ thử vài đơn hàng với các mức phí ship khác nhau để xem công thức đã đúng chưa.
- Chạy sau khi nhận một build đã được sửa nhỏ: thường là sau khi smoke test đã pass, hoặc sau khi dev báo "đã fix bug X, bạn kiểm lại giúp".
- Mục đích chính: xác nhận rằng chức năng/bug được sửa đã hoạt động đúng như mong đợi, trước khi bỏ công sức làm regression đầy đủ.
- Thường không cần lên kịch bản chi tiết (unscripted): vì mỗi bug fix mỗi khác, sanity test hay được thực hiện dựa trên hiểu biết và kinh nghiệm của tester về đúng vùng vừa sửa.
Bảng so sánh nhanh để không nhầm nữa
| Tiêu chí | Smoke Test | Sanity Test |
|---|---|---|
| Mục đích | Build có đủ ổn định để test tiếp? | Thay đổi/bug fix có đúng và hợp lý không? |
| Phạm vi | Rộng, nông (nhiều chức năng chính) | Hẹp, sâu (vài chức năng cụ thể) |
| Thời điểm | Ngay sau mỗi build mới | Sau khi nhận build đã sửa nhỏ |
| Mức độ kịch bản | Thường scripted, dễ tự động hóa | Thường unscripted, dựa kinh nghiệm |
| Câu hỏi trả lời | "Build này còn thở không?" | "Cái sửa này có tỉnh táo không?" |
| Kiểm tra điều gì | Tính ổn định (stability) | Tính hợp lý (rationality) |
Một điểm gây tranh cãi cần biết
Cần thành thật với bạn: trong thực tế công nghiệp, ranh giới giữa smoke và sanity đôi khi bị dùng lẫn lộn, và không phải công ty nào cũng thống nhất. Một số team gọi mọi bài kiểm tra nhanh đầu build là "smoke", và không dùng từ "sanity" bao giờ. Một số tài liệu học thuật lại xem sanity là một tập con của regression test. Điều quan trọng không phải là ai đúng theo từ điển, mà là TEAM CỦA BẠN thống nhất một định nghĩa và dùng nhất quán. Khi đi phỏng vấn ISTQB hay trao đổi chuyên môn, hãy trình bày theo định nghĩa chuẩn ở trên; khi làm việc thực tế, hãy hỏi rõ team đang hiểu từ đó thế nào.
Tình huống thực tế
Tình huống 1 — Sàn thương mại điện tử: smoke chặn một build "chết"
Hãy tưởng tượng một team QA tại một sàn thương mại điện tử kiểu Tiki hoặc Sendo, gọi tạm là "ShopFast". Mỗi sáng, đội DevOps deploy build đêm lên môi trường staging lúc 8h. Đội QA gồm 6 người, mỗi ngày họ chạy một bộ regression khá nặng, tốn khoảng 4 giờ cho toàn bộ.
Một ngày nọ, team quyết định bỏ qua smoke test cho "nhanh" vì đang gấp release cuối sprint. Họ lao thẳng vào regression. Sau 2 tiếng, ba tester lần lượt phát hiện mình không thêm được sản phẩm nào vào giỏ hàng — API giỏ hàng trả về lỗi 500 do một thay đổi cấu hình database đêm qua bị sai. Nghĩa là mọi test case liên quan đến giỏ hàng, thanh toán, đơn hàng đều fail hàng loạt vì một nguyên nhân gốc duy nhất. Hai tiếng và công sức của ba người coi như đổ sông đổ biển.
Bài học rút ra: Nếu ShopFast chạy một smoke test 15 phút vào đầu ngày — chỉ cần kiểm "đăng nhập được, xem sản phẩm được, thêm giỏ hàng được, tới màn thanh toán được" — họ đã phát hiện lỗi giỏ hàng ngay lập tức, reject build, và trả lại cho dev trước khi bất kỳ ai lãng phí thời gian regression. Smoke test không tìm bug sâu; nó bảo vệ thời gian của cả team khỏi những build "chết từ trong trứng".
Tình huống 2 — Ví điện tử: sanity test đúng chỗ vừa sửa
Một team làm ví điện tử (kiểu MoMo hoặc ZaloPay), gọi tạm là "PayViet", phát hiện một bug nghiêm trọng: khi người dùng nạp tiền bằng số tiền có phần lẻ như 50.500đ, hệ thống làm tròn sai thành 50.000đ, gây thất thoát. Bug được báo với severity cao. Dev sửa công thức làm tròn và giao build mới cho QA lúc 3h chiều, kèm ghi chú "chỉ đụng vào module tính tiền nạp".
Chị QA lead không yêu cầu chạy lại toàn bộ regression 4 tiếng ngay lập tức. Thay vào đó, chị giao cho một tester làm sanity test tập trung: thử nạp với hàng loạt số tiền có phần lẻ khác nhau — 50.500đ, 99.999đ, 1.000.001đ, 200.250đ — và kiểm tra số dư sau nạp có khớp từng đồng không. Chỉ trong 20 phút, tester xác nhận công thức đã đúng ở mọi trường hợp thử. Lúc này chị lead mới quyết định lên lịch regression đầy đủ cho ca tối, vì đã đủ tự tin rằng bản sửa "hợp lý".
Bài học rút ra: Sanity test giúp bạn kiểm tra nhanh và sâu đúng vùng vừa thay đổi trước khi đầu tư vào regression tốn kém. Nếu sanity fail (giả sử số dư vẫn sai với 99.999đ), thì làm regression đầy đủ ngay lúc đó là vô nghĩa — trả build lại cho dev là hợp lý hơn. Sanity đóng vai "bộ lọc" tiết kiệm công sức.
Tình huống 3 — Startup fintech: khi tự động hóa smoke test thay đổi cuộc chơi
Một startup fintech ở TP.HCM có 8 lần deploy mỗi ngày do làm việc theo CI/CD. Ban đầu, mỗi lần deploy, một tester phải bấm tay smoke test khoảng 12 phút. Với 8 lần deploy, đó là gần 1.5 giờ mỗi ngày chỉ để "canh build", chưa kể tester dễ mệt và bỏ sót bước.
Team quyết định tự động hóa bộ smoke test — 15 kịch bản chính: đăng nhập, xem số dư, chuyển khoản nội bộ, tra cứu lịch sử giao dịch, đăng xuất... Toàn bộ chạy tự động sau mỗi lần deploy, hoàn tất trong 3 phút, và tự động gửi kết quả pass/fail vào channel Telegram của team. Từ đó, build nào smoke fail sẽ bị chặn tự động, không lên được môi trường test thủ công. Tester được giải phóng khỏi việc bấm tay lặp lại, dành thời gian cho exploratory và test sâu.
Bài học rút ra: Smoke test, vì bản chất lặp lại và ổn định về kịch bản, là ứng viên số một để tự động hóa — đặc biệt trong môi trường deploy liên tục. Ngược lại, sanity test do thay đổi theo từng bug fix nên thường vẫn để tester làm thủ công, tận dụng khả năng phán đoán của con người.
Hướng dẫn từng bước
Đây là quy trình thực tế bạn có thể áp dụng ngay khi nhận một build mới.
Bước 1 — Xác định bạn đang ở tình huống nào. Hỏi: "Đây là build mới toàn bộ (build định kỳ, build lớn) hay là build chỉ sửa một vài thứ cụ thể?" Nếu là build mới toàn bộ → nghiêng về smoke test trước. Nếu là build vá một bug/tính năng cụ thể → nghiêng về sanity test cho đúng vùng đó (thường vẫn nên smoke nhẹ trước để chắc build lên được).
Bước 2 — Với smoke test, liệt kê các "luồng sống còn" (critical paths). Đây là những chức năng mà nếu hỏng thì cả sản phẩm coi như vô dụng: đăng nhập/đăng ký, luồng chính tạo giá trị (đặt hàng, chuyển tiền, đăng bài...), thanh toán, đăng xuất. Với mỗi luồng, viết một bước kiểm tra nông: "thực hiện được đến bước cuối cùng, không cần kiểm chi tiết validation".
Bước 3 — Chạy smoke và ra quyết định gác cổng. Nếu bất kỳ luồng sống còn nào fail → REJECT build, ghi rõ lý do, trả cho dev. Không đi tiếp. Nếu tất cả pass → build "đủ thở", chuyển sang bước tiếp.
Bước 4 — Với sanity test, khoanh vùng đúng cái vừa thay đổi. Đọc changelog/commit message hoặc hỏi dev: "Build này đụng vào những gì?". Chỉ tập trung vào đúng vùng đó và các phần liền kề có khả năng bị ảnh hưởng trực tiếp. Thiết kế vài trường hợp đi sâu vào chi tiết của thay đổi (giá trị biên, các nhánh logic).
Bước 5 — Chạy sanity và ra quyết định tiếp theo. Nếu sanity fail → trả build, không cần regression. Nếu sanity pass → giờ mới đến lúc đầu tư vào regression đầy đủ và các loại test sâu khác.
Bước 6 — Ghi lại kết quả rõ ràng. Trong test report, ghi rõ "Smoke: PASS/FAIL" và "Sanity (module X): PASS/FAIL" tách bạch, để cả team biết chính xác build đã qua những cổng nào.
Lỗi thường gặp & mẹo
Lỗi 1 — Dùng lẫn lộn hai từ trong giao tiếp. Rất nhiều bạn junior nói "em sanity xong rồi ạ" nhưng thực chất mới chỉ smoke qua các luồng chính. Điều này khiến sếp hiểu nhầm mức độ tin cậy. Mẹo: trước khi báo cáo, tự hỏi "mình vừa kiểm RỘNG-NÔNG (smoke) hay HẸP-SÂU đúng chỗ sửa (sanity)?" rồi dùng đúng từ.
Lỗi 2 — Biến smoke test thành một bộ test khổng lồ. Có team nhồi cả trăm case vào "smoke suite" khiến nó chạy mất 40 phút, mất luôn ý nghĩa "nhanh để gác cổng". Mẹo: smoke test nên gói gọn trong 5–15 phút. Nếu dài hơn, bạn đang lẫn nó với regression.
Lỗi 3 — Bỏ qua smoke vì "đang gấp". Như tình huống ShopFast, bỏ smoke để tiết kiệm thời gian thường phản tác dụng, gây tốn kém gấp bội. Mẹo: càng gấp càng phải smoke, vì đó là lúc dễ có build lỗi nhất.
Lỗi 4 — Sanity test lan man ra ngoài vùng thay đổi. Nếu bạn sanity một bug fix về tiền ship mà lại đi test cả tính năng đăng nhập, bạn đang lãng phí và cũng chưa đủ sâu ở đúng chỗ. Mẹo: giữ sanity đúng phạm vi vùng vừa sửa và các phần liền kề trực tiếp; phần còn lại để dành cho regression.
Lỗi 5 — Nghĩ rằng smoke/sanity thay thế được regression. Cả hai đều là bộ lọc đầu vào, không phải kiểm thử đầy đủ. Một build pass smoke và sanity vẫn có thể chứa vô số bug sâu. Mẹo: coi smoke và sanity là "vé vào cửa" cho các loại test kỹ hơn, không phải là điểm dừng.
Mẹo vàng — Tự động hóa smoke, để con người làm sanity. Vì smoke ổn định và lặp lại, hãy ưu tiên tự động hóa nó và gắn vào pipeline deploy. Vì sanity thay đổi theo từng bug fix và cần phán đoán, hãy để tester giàu kinh nghiệm làm thủ công. Đây là cách phân bổ nguồn lực khôn ngoan nhất.
Bài tập thực hành
Bài 1 — Phân loại tình huống. Với mỗi tình huống sau, hãy xác định nên dùng smoke test hay sanity test và giải thích ngắn gọn:
- (a) Sáng thứ Hai, DevOps deploy build định kỳ cuối tuần lên staging, chưa ai chạm vào.
- (b) Dev vừa báo "đã fix bug hiển thị sai ngày sinh trong hồ sơ người dùng".
- (c) Team chuẩn bị chạy bộ regression 5 tiếng cho một build tính năng lớn.
- (d) Sau khi sửa công thức tính điểm tích lũy, cần kiểm nhanh trước khi test sâu.
Bài 3 — Thiết kế sanity test. Giả sử app đặt đồ ăn ở Bài 2 vừa fix bug "áp mã giảm giá không trừ đúng tiền cho đơn trên 200.000đ". Hãy thiết kế 4–5 trường hợp sanity test đi sâu đúng vùng mã giảm giá này (gợi ý: dùng giá trị biên quanh 200.000đ).
Bài 4 — Viết lại báo cáo cho đúng. Một tester viết: "Đã test build mới, mọi thứ ổn." Hãy viết lại câu này thành một câu báo cáo chuyên nghiệp, phân tách rõ smoke và sanity, để người đọc biết chính xác build đã qua cổng nào.
Gợi ý đáp án Bài 1: (a) smoke — build mới toàn bộ, kiểm ổn định trước; (b) sanity — kiểm đúng vùng vừa sửa; (c) smoke trước khi bỏ công regression dài; (d) sanity — kiểm hợp lý của thay đổi cụ thể trước khi test sâu.
Tóm tắt
Smoke test và sanity test đều là những bài kiểm tra nhanh đóng vai "gác cổng", nhưng phục vụ hai mục đích khác nhau. Smoke test kiểm tra tính ổn định của một build mới — nó rộng và nông, chạm vào tất cả luồng sống còn để trả lời câu hỏi "build này có đủ thở để test tiếp không?". Sanity test kiểm tra tính hợp lý của một thay đổi cụ thể — nó hẹp và sâu, tập trung vào đúng vùng vừa được sửa để trả lời câu hỏi "cái sửa này có đúng, có đáng để làm regression tiếp không?".
Cách nhớ đơn giản: smoke là khám tổng quát toàn thân xem còn sống không; sanity là khám kỹ đúng vết mổ vừa xong. Về mặt vận hành, smoke thường được lên kịch bản và rất đáng tự động hóa, chạy sau mỗi build; sanity thường làm thủ công dựa trên kinh nghiệm, chạy sau mỗi bản vá nhỏ. Cả hai đều là bộ lọc đầu vào, không thay thế được regression hay kiểm thử sâu.
Trong thực tế, đừng sa đà vào tranh cãi từ điển — hãy đảm bảo team bạn thống nhất định nghĩa và dùng nhất quán. Quan trọng nhất: dùng đúng từ khi báo cáo, đừng bỏ smoke khi gấp, và giữ mỗi loại test đúng phạm vi của nó. Làm được vậy, bạn đã tiết kiệm cho cả team rất nhiều thời gian và tránh được những hiểu lầm đắt giá.