Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Regression Testing & Test Selection

Software Testing Fundamentals Bài 14/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn đang là tester ở một công ty thương mại điện tử. Đội dev vừa sửa một bug nhỏ ở trang giỏ hàng — chỉ một dòng code thay đổi cách tính phí vận chuyển. Nghe có vẻ vô hại. Nhưng sáng hôm sau, bộ phận chăm sóc khách hàng gọi điện dồn dập: khách đặt hàng thành công nhưng email xác nhận lại hiển thị sai tổng tiền. Hoá ra dòng code "vô hại" kia đã vô tình chạm vào hàm định dạng tiền tệ dùng chung, và hàm đó cũng được template email gọi tới.

Đây chính xác là loại thảm hoạ mà Regression Testing (kiểm thử hồi quy) sinh ra để ngăn chặn. Trong suốt vòng đời của một sản phẩm phần mềm, code không bao giờ đứng yên: có tính năng mới, có sửa lỗi, có nâng cấp thư viện, có tối ưu hiệu năng. Mỗi lần thay đổi là một lần đặt cược rằng những gì đang chạy tốt vẫn tiếp tục chạy tốt. Regression testing là cách chúng ta kiểm chứng canh bạc đó thay vì nhắm mắt hy vọng.

Đây cũng là loại kiểm thử mà một tester sẽ làm nhiều nhất trong sự nghiệp. Feature mới thì thỉnh thoảng mới có, nhưng regression thì lặp đi lặp lại mỗi sprint, mỗi lần release. Nếu bạn làm regression thông minh — biết chọn đúng cái cần test, biết khi nào tự động hoá — bạn tiết kiệm được hàng chục giờ mỗi tuần. Nếu làm ngu ngốc — test lại tất cả mọi thứ mỗi lần, hoặc bỏ sót phần quan trọng — bạn hoặc là kiệt sức, hoặc là để lọt bug ra production. Bài này sẽ dạy bạn cách làm thông minh, đặc biệt là nghệ thuật Test Selection — chọn tập test cần chạy.

Khái niệm cốt lõi

Regression Testing là gì?

Regression testing là việc thực thi lại (re-execute) các test case đã có trên phần chức năng cũ, sau khi hệ thống có thay đổi, để đảm bảo thay đổi đó không phá vỡ những gì đang hoạt động. Từ "regression" nghĩa là "thoái lui" — tức là phần mềm bị lùi từ trạng thái "chạy được" về trạng thái "hỏng". Mục tiêu của chúng ta là phát hiện sự thoái lui đó càng sớm càng tốt.

Điểm mấu chốt cần phân biệt: regression testing không phải để kiểm tra tính năng mới có hoạt động không (đó là confirmation testing / re-testing). Nó tập trung vào tác dụng phụ (side effects) — những vùng code không được đụng tới nhưng vẫn có thể bị ảnh hưởng gián tiếp.

Khi nào cần regression testing?

Bạn cần cân nhắc regression mỗi khi có bất kỳ thay đổi nào chạm vào codebase:

  • Tính năng mới (new feature): code mới có thể dùng chung module, database, hoặc luồng với code cũ.
  • Sửa lỗi (bug fix): đây là nguồn regression phổ biến nhất. Sửa một chỗ, vỡ một chỗ khác.
  • Thay đổi cấu hình / môi trường: đổi version của web server, thư viện, framework, hệ điều hành.
  • Tối ưu hiệu năng / refactor: code được viết lại nhưng đáng lẽ phải giữ nguyên hành vi — đây là lúc regression cực kỳ quan trọng vì "không thay đổi hành vi" chính là thứ cần chứng minh.
  • Hotfix trên production: vá gấp trong lúc sản phẩm đang chạy, rủi ro rất cao.

Confirmation testing vs Regression testing

Hai khái niệm này luôn đi cặp và dễ nhầm:

  • Confirmation testing (re-test): dev báo đã fix bug X. Bạn chạy lại đúng test case đã tìm ra bug X để xác nhận nó thực sự hết. Đây là "kiểm tra chỗ vừa sửa".
  • Regression testing: sau khi confirm bug X đã hết, bạn kiểm tra các chức năng xung quanh để chắc chắn việc sửa X không tạo ra bug mới. Đây là "kiểm tra vùng lân cận".
Quy trình chuẩn khi nhận một bug fix: confirm trước (bug đã hết chưa?), rồi regression sau (có sinh bug mới không?).

Các loại phạm vi regression

Không phải lúc nào cũng chạy tất cả. Trong thực tế, regression được chia theo phạm vi:

  • Full Regression (regression toàn bộ): chạy lại toàn bộ bộ test case. An toàn nhất nhưng tốn kém nhất. Thường dành cho các thay đổi lớn, nâng cấp nền tảng, hoặc release lớn.
  • Partial Regression (regression một phần): chỉ chạy các test liên quan đến vùng bị ảnh hưởng cộng với các module có liên kết. Cân bằng giữa an toàn và chi phí.
  • Regression Test Suite: một tập test case cố định, được tuyển chọn kỹ, đại diện cho các luồng nghiệp vụ quan trọng nhất (critical path). Đây là "bộ khung sườn" chạy mỗi lần release.

Test Selection — trái tim của bài học

Test Selection là kỹ thuật trả lời câu hỏi: "Trong hàng nghìn test case đang có, lần này tôi cần chạy những cái nào?" Chạy tất cả thì lãng phí; chạy quá ít thì rủi ro. Có ba chiến lược chính:

  • Retest All (chạy lại tất cả): đơn giản, an toàn tuyệt đối, nhưng chậm và đắt. Chỉ khả thi khi bộ test nhỏ hoặc đã tự động hoá gần như hoàn toàn.
  • Regression Test Selection (RTS): chỉ chọn tập con test case có khả năng bị ảnh hưởng bởi thay đổi. Dựa trên phân tích tác động (impact analysis) — truy vết xem thay đổi ở module A ảnh hưởng tới những module nào.
  • Test Case Prioritization (ưu tiên test case): không loại bỏ test nào cả, mà sắp xếp thứ tự để chạy những test quan trọng / dễ tìm bug nhất trước. Nếu hết thời gian, ít nhất phần quan trọng đã được chạy. Ưu tiên thường dựa trên: mức độ rủi ro nghiệp vụ, tần suất phát hiện lỗi trong quá khứ, độ phủ code, và tần suất sử dụng của khách hàng.

Impact Analysis — nền tảng của việc chọn đúng

Muốn chọn test đúng, bạn phải hiểu thay đổi này ảnh hưởng tới đâu. Impact analysis là việc lập bản đồ: thay đổi ở đâu → chạm vào module nào → module đó liên quan tính năng nào → tính năng đó có test case nào. Việc này cần sự phối hợp giữa tester và dev. Một câu hỏi vàng bạn nên hỏi dev mỗi lần: "Thay đổi này đụng vào những file / module / API nào dùng chung?"

Tình huống thực tế

Ví dụ 1: MoMo và cái nút "hoàn tiền"

Giả sử một đội QA ở ví điện tử lớn tại Việt Nam nhận task regression cho tính năng hoàn tiền (refund). Ban đầu, dev chỉ đổi giao diện nút "Yêu cầu hoàn tiền" — dời vị trí, đổi màu. Tester non tay có thể nghĩ: "Chỉ là UI, test lại mỗi cái nút là xong."

Nhưng tester giàu kinh nghiệm hỏi impact analysis và phát hiện: việc dời nút kéo theo thay đổi trong component xử lý sự kiện click, và component đó dùng chung với luồng "báo cáo giao dịch lỗi". Kết quả regression đúng phạm vi cần bao gồm: (1) luồng refund từ đầu đến cuối, (2) luồng báo cáo giao dịch lỗi, (3) lịch sử giao dịch hiển thị đúng trạng thái refund.

Khi chạy, họ phát hiện một regression bug: sau khi refund, trạng thái giao dịch trong lịch sử vẫn hiển thị "Thành công" thay vì "Đã hoàn tiền" — vì logic cập nhật trạng thái nằm chung component vừa bị đụng.

Bài học: đừng đánh giá phạm vi regression bằng "kích thước thay đổi bề mặt", hãy đánh giá bằng "phạm vi tác động thực sự". Một thay đổi UI nhỏ vẫn có thể chạm vào logic dùng chung.

Ví dụ 2: Sàn TMĐT nâng cấp thư viện thanh toán

Một sàn thương mại điện tử tại Đông Nam Á (giả định tương tự Tiki hoặc Shopee) quyết định nâng version thư viện xử lý tiền tệ để hỗ trợ thêm loại tiền. Đây là thay đổi ở tầng nền tảng: mọi nơi tính tiền đều bị ảnh hưởng — giá sản phẩm, giỏ hàng, phí ship, mã giảm giá, email hoá đơn, báo cáo doanh thu.

Trong tình huống này, chiến lược Retest All (hoặc gần như full regression) là hợp lý, vì impact analysis cho thấy diện ảnh hưởng quá rộng để chọn lọc an toàn. Đội QA đã có sẵn 1.200 test case tự động cho toàn bộ luồng tiền tệ, chạy qua CI trong 40 phút. Kết quả: 3 test fail liên quan làm tròn số tiền — thư viện mới làm tròn theo quy tắc khác (ví dụ 10.500,5 đồng), gây lệch 1 đồng ở một số đơn hàng. Lệch nhỏ nhưng với kế toán là thảm hoạ.

Bài học: với thay đổi ở tầng nền tảng / dùng chung diện rộng, đừng cố tiết kiệm bằng partial regression. Đây chính là lúc khoản đầu tư vào tự động hoá regression phát huy giá trị — full regression thủ công 1.200 case là bất khả thi, nhưng máy chạy 40 phút là chuyện nhỏ.

Ví dụ 3: Startup thiếu người và bài toán prioritization

Một startup fintech Việt Nam chỉ có 2 tester, release mỗi 2 tuần, và bộ regression thủ công tốn 3 ngày nếu chạy hết. Họ không có đủ thời gian retest all trước mỗi release. Giải pháp của họ là Test Case Prioritization kết hợp phân tầng rủi ro:

  • Tầng P0 (bắt buộc chạy mọi lần): đăng ký/đăng nhập, nạp tiền, chuyển tiền, rút tiền — luồng chạm trực tiếp tới tiền của khách. Khoảng 60 case, chạy trong nửa ngày.
  • Tầng P1 (chạy nếu có thời gian / nếu vùng liên quan bị đụng): lịch sử giao dịch, thông báo, hồ sơ cá nhân.
  • Tầng P2 (chạy định kỳ mỗi tháng, không bắt buộc mỗi release): cài đặt giao diện, mục trợ giúp.
Nhờ vậy, dù không chạy hết, họ vẫn đảm bảo mọi thứ động chạm tới tiền đều được kiểm mỗi lần release. Trong 6 tháng, không có bug tiền nào lọt production — dù thỉnh thoảng vẫn có bug nhỏ ở P2.

Bài học: khi nguồn lực hữu hạn (rất phổ biến ở Việt Nam), prioritization theo rủi ro nghiệp vụ giúp bạn "thua ở chỗ ít quan trọng để thắng ở chỗ sống còn".

Hướng dẫn từng bước

Đây là quy trình chuẩn để thực hiện một đợt regression testing hiệu quả:

Bước 1 — Thu thập thông tin thay đổi. Đọc kỹ changelog, commit, mô tả bug fix hoặc feature. Hỏi dev trực tiếp: thay đổi này đụng vào file/module/API/bảng dữ liệu nào? Có dùng component/hàm dùng chung không?

Bước 2 — Phân tích tác động (Impact Analysis). Vẽ ra bản đồ: thay đổi → module bị chạm → tính năng liên quan → luồng nghiệp vụ liên quan. Đây là bước quyết định phạm vi. Đừng bỏ qua các liên kết gián tiếp (dữ liệu dùng chung, event dùng chung, API dùng chung).

Bước 3 — Chọn chiến lược Test Selection. Dựa trên diện ảnh hưởng và nguồn lực, quyết định: Retest All (thay đổi nền tảng, diện rộng), RTS chọn lọc (thay đổi cục bộ, có bản đồ tác động rõ), hay Prioritization (thiếu thời gian). Thường bạn kết hợp cả ba.

Bước 4 — Xây dựng danh sách test case cần chạy. Từ bộ regression suite, lọc ra các case theo phạm vi đã chọn. Luôn kèm theo tập "critical path" P0 bất kể thay đổi ở đâu — đây là mạng lưới an toàn.

Bước 5 — Confirm trước, Regression sau. Với bug fix: chạy confirmation test để xác nhận bug đã hết, rồi mới chạy regression quanh vùng đó.

Bước 6 — Thực thi và ghi nhận. Chạy test, log kết quả rõ ràng: pass/fail, môi trường, version build. Mọi fail cần được đánh giá: đây là regression thật hay do dữ liệu/môi trường?

Bước 7 — Báo cáo và ra quyết định release. Tổng hợp: đã chạy bao nhiêu case, bao nhiêu fail, có regression nghiêm trọng không. Cung cấp thông tin để team quyết định "go / no-go" cho release.

Bước 8 — Bảo trì regression suite. Sau mỗi bug thoát ra production, hãy bổ sung test case bắt được bug đó vào suite. Đồng thời loại bỏ các case lỗi thời. Regression suite là tài sản sống, cần chăm sóc liên tục.

Lỗi thường gặp & mẹo

Lỗi 1 — Đánh đồng kích thước code với phạm vi tác động. "Chỉ sửa 1 dòng thì test 1 chỗ." Sai. Một dòng đụng vào hàm dùng chung có thể ảnh hưởng cả chục tính năng. Luôn làm impact analysis.

Lỗi 2 — Regression suite phình to vô tội vạ. Cứ thêm case mà không bao giờ dọn, đến lúc suite chạy mất cả ngày và đầy case trùng lặp, lỗi thời. Mẹo: định kỳ rà soát, gộp case trùng, xoá case cho tính năng đã bỏ.

Lỗi 3 — Bỏ qua confirmation testing. Nhảy thẳng vào regression mà chưa xác nhận bug gốc đã thực sự hết. Kết quả: tưởng ổn nhưng bug vẫn còn.

Lỗi 4 — Không phân biệt fail do regression với fail do môi trường/dữ liệu. Test fail chưa chắc là regression. Có thể do dữ liệu test cũ, môi trường lỗi. Luôn điều tra nguyên nhân trước khi báo bug.

Lỗi 5 — Dựa hoàn toàn vào regression thủ công. Với sản phẩm trưởng thành, regression thủ công không scale. Đây là loại kiểm thử lặp lại nhiều nhất, nên là ứng viên số một cho tự động hoá. Hãy tự động hoá tập P0/critical path trước.

Mẹo vàng: Ưu tiên tự động hoá theo thứ tự — luồng ổn định, chạy thường xuyên, rủi ro nghiệp vụ cao. Đừng tự động hoá tính năng còn thay đổi liên tục vì test sẽ vỡ suốt và bạn tốn công bảo trì hơn là lợi ích thu được.

Mẹo về dữ liệu: Regression cần dữ liệu test ổn định, có thể lặp lại. Nếu mỗi lần chạy dữ liệu một khác, bạn không thể phân biệt fail thật với fail do dữ liệu.

Bài tập thực hành

Bài 1 — Impact Analysis. Giả sử bạn là tester của một app đặt đồ ăn. Dev báo: "Đã đổi công thức tính khoảng cách giao hàng để tính phí ship chính xác hơn." Hãy liệt kê ít nhất 5 tính năng/luồng bạn cần đưa vào phạm vi regression và giải thích lý do từng cái.

Bài 2 — Test Selection. Bạn có 500 test case regression, chỉ có 4 giờ trước khi release. Thay đổi lần này là một bug fix cục bộ ở tính năng "đánh giá sản phẩm" (review). Hãy mô tả bạn sẽ dùng chiến lược nào (Retest All / RTS / Prioritization), chọn nhóm case nào chạy trước, và vì sao.

Bài 3 — Xây tầng ưu tiên. Chọn một sản phẩm bạn quen thuộc (ví dụ ứng dụng ngân hàng, sàn TMĐT). Hãy phân loại các tính năng của nó thành 3 tầng P0 / P1 / P2 cho regression suite, và giải thích tiêu chí phân tầng.

Bài 4 — Phản biện. Một đồng nghiệp nói: "Cứ retest all mỗi lần cho chắc, khỏi cần phân tích." Hãy viết 3 lý do phản biện và nêu trường hợp nào thì retest all thực sự hợp lý.

Tóm tắt

Regression testing là việc chạy lại các test đã có sau mỗi thay đổi, nhằm phát hiện tác dụng phụ — những chỗ đang chạy tốt bỗng bị vỡ. Nó khác với confirmation testing (kiểm tra chính chỗ vừa sửa) và là loại kiểm thử bạn sẽ làm nhiều nhất trong nghề.

Chìa khoá để làm regression thông minh nằm ở Test Selection: dùng impact analysis để hiểu thay đổi ảnh hưởng tới đâu, rồi chọn chiến lược phù hợp — Retest All cho thay đổi nền tảng diện rộng, Regression Test Selection cho thay đổi cục bộ, và Test Case Prioritization khi thiếu thời gian. Trong mọi trường hợp, luôn giữ một tập critical path (P0) làm mạng lưới an toàn.

Ba nguyên tắc mang về: (1) đánh giá phạm vi bằng tác động thực sự, không bằng kích thước code; (2) ưu tiên theo rủi ro nghiệp vụ khi nguồn lực hữu hạn; (3) tự động hoá tập test lặp lại ổn định để regression không bao giờ trở thành nút thắt cổ chai. Làm chủ được ba điều này, bạn sẽ vừa bảo vệ được chất lượng sản phẩm, vừa không kiệt sức vì test thủ công vô tận.