Static Testing và Dynamic Testing: hai cách bắt lỗi, một mục tiêu chất lượng

August 15, 2026
Static Testing và Dynamic Testing: hai cách bắt lỗi, một mục tiêu chất lượng

Xem nhanh

Một test case pass hết. Sản phẩm được release đúng hạn. Ai cũng thở phào.

Hai ngày sau, khách hàng báo một lỗi mà không ai bắt được trong lúc test. Truy ngược lại mới thấy nguyên nhân nằm ở một dòng đặc tả mơ hồ — viết từ trước khi dòng code đầu tiên ra đời. Không ai chạy thử ra được lỗi đó, đơn giản vì nó không phải lỗi khi chạy. Nó là lỗi trên giấy.

Đây chính là lý do trong kiểm thử phần mềm, không phải lúc nào "test" cũng đồng nghĩa với việc mở ứng dụng lên và bấm nút. Theo ISTQB, kiểm thử chia thành hai nhánh lớn: Static Testing (Kiểm thử tĩnh)Dynamic Testing (Kiểm thử động). Một loại bắt lỗi mà không cần chạy chương trình. Loại còn lại bắt lỗi khi chương trình thật sự vận hành. Hiểu được ranh giới này, bạn sẽ biết bắt lỗi sớm hơnđúng chỗ hơn.

Điểm khác biệt nằm ở một câu hỏi

Mọi định nghĩa dài dòng đều quy về một câu hỏi duy nhất: bạn có cần chạy chương trình lên hay không?

Ở đây, "chạy chương trình" nghĩa là thực thi phần mềm đã được build — mở ứng dụng lên, cấp dữ liệu đầu vào cho nó xử lý, để máy thật sự thực thi đoạn code đó. Ví dụ: mở website lên bấm nút, gọi một API và nhận phản hồi, hay cho một hàm chạy với dữ liệu cụ thể. Ngược lại, không chạy chương trình là khi bạn chỉ đọc và phân tích — tài liệu yêu cầu, bản thiết kế, hay chính mã nguồn ở dạng văn bản — mà không bắt máy thực thi gì cả.

  • Có chạy → Dynamic Testing.
  • Không chạy → Static Testing.

Nghe thì đơn giản, nhưng hệ quả của nó lại rất lớn — vì hai cách tiếp cận này bắt được hai loại lỗi hoàn toàn khác nhau, ở hai thời điểm khác nhau.

Lấy luôn một tình huống quen thuộc trong phát triển phần mềm: đội vừa nhận một user story (mô tả ngắn một yêu cầu từ góc nhìn người dùng) cho chức năng "đặt lại mật khẩu". Trước khi ai viết một dòng code, cả nhóm ngồi review tài liệu yêu cầu: link reset có thời hạn bao lâu? Nếu người dùng bấm link đã hết hạn thì sao? Nếu yêu cầu reset hai lần liên tiếp thì link cũ còn hiệu lực không? Hóa ra đặc tả không hề nhắc đến trường hợp link hết hạn. Một lỗ hổng được bịt lại — mà chưa cần chạy bất cứ thứ gì. Đó là Static Testing.

Sau khi chức năng được code xong, tester mới thực thi nó: nhập email, bấm link trong hộp thư, đổi mật khẩu, rồi đăng nhập lại bằng mật khẩu mới. Lúc này mới phát hiện link reset vẫn dùng được sau khi đã đổi mật khẩu thành công — một lỗi chỉ lộ ra khi hệ thống thật sự chạy. Đó là Dynamic Testing.

Trường hợp đặc tả bỏ sót thì có đọc kỹ tài liệu mới thấy. Còn lỗi link vẫn sống sau khi đổi mật khẩu thì phải chạy thật mới bắt được. Không cách nào thay thế hoàn toàn cách nào.

Static Testing - Kiểm thử tĩnh

Theo ISTQB, Static Testing là việc kiểm tra một sản phẩm phần mềm hoặc tài liệu liên quan mà không thực thi chương trình. Nói gọn lại: bạn "soi" tài liệu và mã nguồn để tìm vấn đề, thay vì chạy ứng dụng.

Đối tượng của nó rộng hơn nhiều người tưởng — không chỉ là code, mà cả tài liệu yêu cầu, user story, tài liệu thiết kế, test case, thậm chí tài liệu hướng dẫn người dùng. Và nó có hai hình thức.

Thứ nhất là Review — con người đọc và đánh giá. ISTQB phân theo mức độ trang trọng tăng dần: Informal Review (trao đổi nhanh), Walkthrough (tác giả dẫn mọi người đi qua tài liệu), Technical Review (chuyên gia kỹ thuật đánh giá), và Inspection (trang trọng nhất, có quy trình và vai trò rõ ràng).

Thứ hai là Static Analysis — dùng công cụ tự động phân tích mã nguồn mà không chạy: phát hiện biến không dùng, code không bao giờ được thực thi, vi phạm coding convention (quy ước viết code chung của nhóm), hay lỗ hổng bảo mật tiềm ẩn.

Đây là chỗ mình muốn nói thẳng một điều: nhiều người xem Static Testing là việc phụ. "Đọc tài liệu thì test cái gì?" Nhưng thực tế, đây thường là nơi chặn được những lỗi mà nếu lọt qua sẽ tốn kém nhất để sửa về sau — vì nó tóm được thứ mà Dynamic Testing gần như bó tay:

  • Yêu cầu mơ hồ, mỗi người hiểu một kiểu.
  • Hai yêu cầu mâu thuẫn nhau trong cùng một tài liệu.
  • Một điều kiện, một trường hợp bị bỏ sót hoàn toàn.

Bạn không thể "chạy" một yêu cầu bị bỏ sót để phát hiện ra nó. Nó không tồn tại trong code để mà chạy. Chỉ có đọc và rà soát mới tìm thấy — và tìm thấy trước khi nó kịp biến thành một lỗi thật.

Dynamic Testing - Kiểm thử động

Static Testing dừng ở giấy. Dynamic Testing thì ngược lại: nó là kiểm thử bằng cách thực thi phần mềm trên một tập dữ liệu đầu vào, rồi so sánh kết quả thực tế với kết quả mong đợi.

Đây chính là phần mà hầu hết mọi người hình dung khi nghĩ về kiểm thử — mở ứng dụng, nhập dữ liệu, thao tác, xem kết quả. Nó bao trùm gần như mọi loại test cần chạy chương trình:

  • Kiểm thử chức năng (Functional Testing): tính năng có làm đúng việc của nó không.
  • Kiểm thử phi chức năng (Non-functional Testing): hiệu năng, bảo mật, khả năng tương thích, chịu tải...

Các kỹ thuật thiết kế test quen thuộc — Equivalence Partitioning, Boundary Value Analysis, Decision Table — cũng nằm ở đây, vì chúng dùng để chọn dữ liệu cho việc chạy test.

Dynamic Testing trả lời câu hỏi mà Static Testing không bao giờ trả lời được: khi phần mềm thật sự chạy, nó có hành xử đúng không? Có những lỗi cứng đầu chỉ chịu lộ diện khi hệ thống vận hành — lỗi tích hợp giữa các module, lỗi phụ thuộc môi trường, hay hành vi sai khi dữ liệu thật chạy qua nhiều bước xử lý liên tiếp.

Lấy một trang thương mại điện tử làm ví dụ. Bạn thêm sản phẩm vào giỏ, đổi số lượng, và kiểm tra tổng tiền có đúng không. Bạn đặt hàng và xác nhận email gửi đến đúng địa chỉ. Bạn mô phỏng hàng nghìn người truy cập cùng lúc để xem hệ thống có sập không. Tất cả những điều đó — bạn phải chạy mới biết.

Đặt cạnh nhau

Một lưu ý nhỏ về thuật ngữ trước khi so sánh: trong kiểm thử, defect là lỗi nằm sẵn trong sản phẩm (ví dụ một dòng đặc tả viết sai), còn failure là biểu hiện sai lệch quan sát được khi phần mềm chạy. Nói ngắn gọn: Static Testing bắt defect ngay ở nguồn, Dynamic Testing bắt failure khi vận hành.

Tiêu chí Static Testing Dynamic Testing
Chạy chương trình? Không
Đối tượng Tài liệu, đặc tả, mã nguồn, test case Phần mềm đang chạy
Loại lỗi tìm được Lỗi trong tài liệu/thiết kế — defect ở nguồn (mơ hồ, mâu thuẫn, thiếu sót) Lỗi lộ ra khi chạy — failure (kết quả sai, hành vi lỗi)
Thời điểm áp dụng Rất sớm, ngay khi có tài liệu Sau khi đã có phần mềm chạy được
Chi phí sửa lỗi Thấp — sửa khi còn trên giấy Cao hơn — lỗi đã đi vào code
Ai thực hiện Tester, dev, BA (Business Analyst), reviewer Chủ yếu là tester, dev
Ví dụ Review user story, inspection thiết kế, static analysis Chạy test case, performance testing, integration testing

Vì sao không thể chọn một

Lý do thứ nhất là chi phí. Một yêu cầu mơ hồ nếu bị tóm ngay lúc review chỉ tốn một dòng sửa tài liệu; nếu lọt qua thì cái giá sửa nó tăng lên rất nhanh khi sản phẩm càng tiến xa. Đây chính là nguyên tắc "Early Testing" của ISTQB — bắt đầu kiểm thử càng sớm càng tốt — và Static Testing là thứ làm được điều đó sớm nhất, vì nó chẳng cần đợi code chạy được. (Mình có nói kỹ vì sao bắt lỗi sớm lại rẻ hơn trong một bài riêng về Early Testing.)

Nhưng lý do quan trọng hơn cả chi phí: hai loại này tìm ra hai loại lỗi khác nhau. Static Testing giỏi bắt thiếu sót, mâu thuẫn, mơ hồ trong tài liệu. Dynamic Testing giỏi bắt lỗi chỉ xuất hiện khi hệ thống thật sự vận hành. Chúng không cạnh tranh nhau — chúng vá vào đúng những lỗ hổng mà loại kia để lại.

Điều này càng rõ với các sản phẩm doanh nghiệp cài đặt tại chỗ (on-premise), khi phần mềm phải chạy trên nhiều hệ điều hành, nhiều cấu hình và nhiều mức phân quyền khác nhau. Chạy thử mọi tổ hợp môi trường là gần như bất khả thi. Vì vậy, đọc và rà soát kỹ đặc tả trước khi viết test case không phải là bước "cho có" — nó là cách chặn lỗi từ gốc, khi mọi thứ còn rẻ để sửa.

Lời kết

Static Testing và Dynamic Testing không phải hai ô để bạn tick chọn một. Chúng là hai lớp phòng thủ đứng ở hai thời điểm khác nhau: một lớp gác cổng khi ý tưởng còn trên giấy, một lớp gác cổng khi sản phẩm đã chạy thật. Bỏ lớp nào, bạn cũng để hở một khoảng.

Quay lại câu chuyện đầu bài — lỗi lọt ra production vì một dòng đặc tả mơ hồ. Test case pass hết là thật. Nhưng "pass hết test" và "không còn lỗi" là hai chuyện khác nhau. Một quy trình trưởng thành sẽ rà soát đặc tả thật kỹ để bắt lỗi sớm, đồng thời thực thi test có chiến lược để kiểm chứng hành vi thật.

Còn bạn? Hãy thử nhìn lại quy trình của mình và tự kiểm: bạn đang dùng cả hai lớp phòng thủ, hay chỉ mới một? Nếu lâu nay bạn chỉ test khi đã có sản phẩm chạy được, thì có lẽ còn cả một lớp bảo vệ — rẻ và hiệu quả — đang bị bỏ ngỏ ngay từ khâu đọc tài liệu.

Cảm ơn bạn đã đọc đến đây. Chúc bạn test vui!