Chọn đến phần học sinh cần nhanh chóng thông qua mục lục bằng cách click đến phần đó
- I. Lý thuyết trọng tâm
- 1. Tại sao cần kiểm thử và gỡ lỗi?
- 2. Bốn phương pháp kiểm thử chương trình
- 3. Ví dụ minh hoạ: Kiểm thử chương trình tính ƯCLN
- II. Hướng dẫn thực hành
- Ví dụ thực hành: Kiểm thử chương trình tính ƯCLN
- III. Phương pháp giải các dạng bài tập
- Dạng 1: Nhận biết lỗi ngoại lệ và đề xuất cách xử lý
- Dạng 2: Tìm và sửa lỗi trong chương trình cho sẵn
- Dạng 3: Đánh giá phương pháp kiểm thử
- IV. Sơ đồ tư duy
- V. Bảng sai lầm thường gặp
I. Lý thuyết trọng tâm
1. Tại sao cần kiểm thử và gỡ lỗi?
Một chương trình chạy không bị dừng (không có lỗi ngoại lệ) chưa chắc đã đúng. Nhiều lỗi ẩn bên trong – gọi là bug – khiến kết quả sai mà không hề có thông báo lỗi. Những bug này thường khó phát hiện hơn lỗi cú pháp hay lỗi ngoại lệ rất nhiều.
Vì vậy, sau khi viết xong chương trình, ta cần kiểm thử (test) để tìm lỗi và gỡ lỗi (debug) để sửa chúng.
2. Bốn phương pháp kiểm thử chương trình
a) Quan sát mã lỗi ngoại lệ (Runtime Error)
Khi chương trình bị dừng giữa chừng, Python hiển thị mã lỗi ngoại lệ kèm vị trí dòng lệnh gây lỗi. Dựa vào thông tin này, ta xác định nguyên nhân rồi sửa.
Cách thực hiện: Đọc kỹ dòng Traceback → xác định tên file, số dòng, mã lỗi → phân tích và sửa.
b) Kiểm thử bằng các bộ dữ liệu test
Chuẩn bị các bộ dữ liệu đầu vào kèm kết quả đầu ra đã biết trước, sau đó chạy chương trình và đối chiếu. Khi thiết kế bộ test cần lưu ý:
- Đa dạng: Cần nhiều bộ test với kích thước và tính chất dữ liệu khác nhau.
- Ngẫu nhiên: Sinh dữ liệu đầu vào ngẫu nhiên trong miền xác định giúp tăng khả năng phát hiện lỗi.
- Vùng biên: Đặc biệt quan trọng – kiểm tra ở các giá trị cực trị, giá trị ranh giới. Ví dụ: nếu đầu vào là cặp $(x, y)$ với $0 \leq x, y \leq 1$, cần test với $(0, 0)$, $(0, 1)$, $(1, 0)$, $(1, 1)$. Thực tế cho thấy lỗi thường xuất hiện tại vùng biên hoặc lân cận biên.
c) In các giá trị trung gian
Chèn thêm lệnh print() vào giữa chương trình để in ra giá trị của các biến tại từng bước. Qua đó, ta theo dõi được quá trình tính toán và phát hiện chỗ sai.
Giả sử chương trình có đầu vào $(x_1, x_2, …, x_n)$, đầu ra $(a_1, a_2, …, a_m)$ và sử dụng các biến trung gian $(y_1, y_2, …, y_k)$. Với mỗi bộ test, ta bổ sung lệnh in để quan sát:
$$(x_1, x_2, …, x_n) \rightarrow (y_1, y_2, …, y_k) \rightarrow (a_1, a_2, …, a_m)$$
Nếu kết quả cuối sai, ta dễ dàng xác định lỗi nằm ở giai đoạn nào.
d) Sử dụng điểm dừng (Breakpoint)
Công cụ breakpoint cho phép đặt các “điểm dừng” tại những vị trí mong muốn trong chương trình. Khi chạy, chương trình tạm dừng tại các điểm này, cho phép người kiểm thử quan sát giá trị các biến và trạng thái chương trình.
Trên thực tế, kết hợp điểm dừng với in giá trị trung gian sẽ cho hiệu quả kiểm thử cao nhất.
3. Ví dụ minh hoạ: Kiểm thử chương trình tính ƯCLN
Bài toán: Nhập hai số tự nhiên $m$, $n$, tính ƯCLN dựa trên thuật toán:
- $\gcd(m, m) = m$
- Nếu $n > m$: $\gcd(m, n) = \gcd(m, n – m)$
- Nếu $n < m$: $\gcd(m, n) = \gcd(m – n, n)$
Chương trình:
# Tính ƯCLN của m, n
m = int(input("Nhập số tự nhiên m: "))
n = int(input("Nhập số tự nhiên n: "))
while m != n:
if m < n:
n = n - m
else:
m = m - n
print("Đáp số:", m)
Cách 1: In giá trị trung gian
Bổ sung biến đếm k và lệnh print() để theo dõi sự thay đổi của m, n qua mỗi vòng lặp:
# Tính ƯCLN của m, n
m = int(input("Nhập số tự nhiên m: "))
n = int(input("Nhập số tự nhiên n: "))
k = 0
while m != n:
k = k + 1
print("Vòng lặp", k, ":", m, n)
if m < n:
n = n - m
else:
m = m - n
print("Kết thúc vòng lặp:", m, n)
print("Đáp số:", m)
Kết quả với $m = 20$, $n = 16$:
Vòng lặp 1 : 20 16
Vòng lặp 2 : 4 16
Vòng lặp 3 : 4 12
Vòng lặp 4 : 4 8
Kết thúc vòng lặp: 4 4
Đáp số: 4
Quan sát sự thay đổi của m, n qua từng vòng lặp giúp xác nhận thuật toán hoạt động đúng.
Cách 2: Sử dụng điểm dừng
Đặt breakpoint tại dòng while m != n: (dòng 4). Mỗi lần chương trình dừng, ghi lại giá trị m, n vào bảng:
| Vòng lặp | m | n | Kết quả |
|---|---|---|---|
| 1 | 20 | 16 | |
| 2 | 4 | 16 | |
| 3 | 4 | 12 | |
| 4 | 4 | 8 | |
| Kết thúc lặp | 4 | 4 | 4 |
Cả hai cách đều hiệu quả trong việc kiểm soát và phát hiện lỗi chương trình.
II. Hướng dẫn thực hành
Ví dụ thực hành: Kiểm thử chương trình tính ƯCLN
Mục tiêu: Áp dụng phương pháp in giá trị trung gian và điểm dừng để kiểm thử chương trình tính ƯCLN của hai số tự nhiên.
Bước 1: Viết chương trình gốc (không có lệnh kiểm thử):
m = int(input("Nhập số tự nhiên m: "))
n = int(input("Nhập số tự nhiên n: "))
while m != n:
if m < n:
n = n - m
else:
m = m - n
print("Đáp số:", m)
Bước 2: Bổ sung biến đếm k và lệnh print() để in giá trị trung gian:
m = int(input("Nhập số tự nhiên m: "))
n = int(input("Nhập số tự nhiên n: "))
k = 0
while m != n:
k = k + 1
print("Vòng lặp", k, ":", m, n)
if m < n:
n = n - m
else:
m = m - n
print("Kết thúc vòng lặp:", m, n)
print("Đáp số:", m)
Bước 3: Chạy thử với $m = 20$, $n = 16$ và theo dõi kết quả qua từng vòng lặp. Đối chiếu với phép tính tay: $\gcd(20, 16) = \gcd(4, 16) = \gcd(4, 12) = \gcd(4, 8) = \gcd(4, 4) = 4$ ✓
Bước 4: Thử thêm các bộ test khác, đặc biệt vùng biên:
| Bộ test | Kết quả mong đợi | Ghi chú |
|---|---|---|
| $m = 12$, $n = 8$ | $4$ | Trường hợp thông thường |
| $m = 7$, $n = 7$ | $7$ | Hai số bằng nhau |
| $m = 1$, $n = 15$ | $1$ | Một số bằng $1$ |
| $m = 0$, $n = 5$ | Vòng lặp vô hạn! | ⚠ Vùng biên – cần xử lý riêng |
III. Phương pháp giải các dạng bài tập
Dạng 1: Nhận biết lỗi ngoại lệ và đề xuất cách xử lý
Phương pháp chung:
- Xác định mã lỗi ngoại lệ thuộc loại nào (tra bảng mã lỗi đã học ở Bài 29).
- Phân tích nguyên nhân gốc: dữ liệu đầu vào không hợp lệ? phép toán sai kiểu? chia cho $0$?
- Đề xuất cách khắc phục: thêm kiểm tra điều kiện trước khi thực hiện phép toán, chuyển đổi kiểu dữ liệu đúng cách.
Bài mẫu – Luyện tập 1:
Chương trình khi chạy phát sinh lỗi ZeroDivisionError. Đó là lỗi gì và xử lý như thế nào?
Lời giải:
ZeroDivisionError là lỗi ngoại lệ xảy ra khi chương trình thực hiện phép chia cho giá trị $0$.
Cách xử lý: Trước khi thực hiện phép chia, cần kiểm tra số chia có bằng $0$ hay không:
if n != 0:
kq = m / n
print("Kết quả:", kq)
else:
print("Lỗi: không thể chia cho 0!")
Dạng 2: Tìm và sửa lỗi trong chương trình cho sẵn
Phương pháp chung:
- Bước 1: Đọc kỹ đề bài để hiểu rõ chương trình được yêu cầu làm gì.
- Bước 2: Chạy thử chương trình “trong đầu” với một bộ dữ liệu cụ thể, ghi lại kết quả từng bước.
- Bước 3: So sánh kết quả chạy thử với kết quả đúng → xác định dòng lệnh gây sai.
- Bước 4: Phân loại lỗi (cú pháp, ngoại lệ, hay logic) và đề xuất cách sửa.
- Đặc biệt chú ý: lệnh
input()trả về xâu kí tự, phạm virange(), điều kiện vòng lặpwhile.
Bài mẫu 1 – Luyện tập 2:
Chương trình sau có lỗi không? Nếu có thì tìm và sửa lỗi:
m = input("Nhập số tự nhiên m: ")
n = input("Nhập số tự nhiên n: ")
print("Tổng hai số đã nhập là:", m + n)
Lời giải:
Chương trình có lỗi logic. Hàm input() trả về kiểu xâu kí tự (str), nên phép m + n thực hiện nối xâu chứ không phải cộng số.
Ví dụ: Nếu nhập m = 3, n = 5, kết quả in ra sẽ là "35" thay vì 8.
Cách sửa: Chuyển đổi kiểu dữ liệu bằng int():
m = int(input("Nhập số tự nhiên m: "))
n = int(input("Nhập số tự nhiên n: "))
print("Tổng hai số đã nhập là:", m + n)
Bài mẫu 2 – Vận dụng 1:
Chương trình sau có chức năng sắp xếp dãy số tăng dần. Kiểm tra xem có lỗi không, nếu có thì tìm và sửa:
A = [10, 1, 5, 2, 8, 0, 4]
for i in range(len(A) - 1):
j = i
while j > 1 and A[j] < A[j - 1]:
A[j], A[j - 1] = A[j - 1], A[j]
j = j - 1
print(A)
Lời giải:
Chương trình có lỗi logic – kết quả sắp xếp không đúng. Có hai lỗi cần sửa:
Lỗi 1: Điều kiện j > 1 sai → phải là j > 0.
Với j > 1, khi j = 1 vòng while dừng ngay, khiến phần tử tại vị trí đầu tiên (chỉ số $0$) không bao giờ được so sánh và hoán đổi. Đổi thành j > 0 thì phần tử sẽ được đẩy về đúng vị trí đầu danh sách khi cần.
Lỗi 2: range(len(A) - 1) chỉ duyệt $i$ từ $0$ đến $5$, bỏ sót phần tử cuối cùng (chỉ số $6$, giá trị $4$) không được xử lý. Cần đổi thành range(1, len(A)).
Chương trình sau khi sửa:
A = [10, 1, 5, 2, 8, 0, 4]
for i in range(1, len(A)):
j = i
while j > 0 and A[j] < A[j - 1]:
A[j], A[j - 1] = A[j - 1], A[j]
j = j - 1
print(A)
Kết quả: [0, 1, 2, 4, 5, 8, 10] ✓
Kiểm chứng bằng chạy tay với bản sửa:
| Vòng $i$ | Trạng thái danh sách |
|---|---|
| Ban đầu | [10, 1, 5, 2, 8, 0, 4] |
| $i = 1$ | [1, 10, 5, 2, 8, 0, 4] |
| $i = 2$ | [1, 5, 10, 2, 8, 0, 4] |
| $i = 3$ | [1, 2, 5, 10, 8, 0, 4] |
| $i = 4$ | [1, 2, 5, 8, 10, 0, 4] |
| $i = 5$ | [0, 1, 2, 5, 8, 10, 4] |
| $i = 6$ | [0, 1, 2, 4, 5, 8, 10] |
Dạng 3: Đánh giá phương pháp kiểm thử
Phương pháp chung:
- Hiểu rõ ưu điểm và hạn chế của từng phương pháp kiểm thử.
- Nhận thức rằng không phương pháp nào đảm bảo tìm hết 100% lỗi.
- Cần kết hợp nhiều phương pháp để tăng khả năng phát hiện lỗi.
Bài mẫu – Vận dụng 2:
Để kiểm thử chương trình, nếu chỉ dùng các bộ dữ liệu test thì có đảm bảo tìm ra hết lỗi không? Vì sao?
Lời giải:
Không thể đảm bảo tìm hết tất cả lỗi chỉ bằng kiểm thử qua bộ dữ liệu test. Lý do:
- Miền dữ liệu đầu vào thường rất rộng (thậm chí vô hạn), ta không thể thử hết mọi trường hợp.
- Các bộ test chỉ kiểm tra được những trường hợp cụ thể đã chọn. Lỗi có thể nằm ở những tổ hợp dữ liệu mà ta chưa nghĩ đến.
- Chương trình có thể đúng với tất cả bộ test đã thử nhưng vẫn sai với dữ liệu khác.
Vì vậy, cần kết hợp nhiều phương pháp: kiểm thử bằng dữ liệu test (đặc biệt vùng biên), in giá trị trung gian, sử dụng điểm dừng, và đọc lại mã nguồn để phân tích logic.
IV. Sơ đồ tư duy
- Quan sát mã lỗi ngoại lệ
- 1.1. Đọc thông báo
Tracebackkhi chương trình bị dừng - 1.2. Xác định dòng lệnh gây lỗi và mã lỗi cụ thể
- 1.3. Phân tích nguyên nhân → sửa lỗi
- 1.1. Đọc thông báo
- Kiểm thử bằng bộ dữ liệu test
- 2.1. Chuẩn bị nhiều bộ test đa dạng
- 2.2. Bộ test ngẫu nhiên – sinh dữ liệu tự động
- 2.3. Bộ test vùng biên – giá trị cực trị, ranh giới
- 2.4. Đối chiếu kết quả chương trình với đáp án đã biết
- In giá trị trung gian
- 3.1. Chèn lệnh
print()tại các vị trí quan trọng - 3.2. Theo dõi giá trị biến qua từng bước
- 3.3. So sánh giá trị trung gian với kết quả mong đợi
- 3.1. Chèn lệnh
- Điểm dừng (Breakpoint)
- 4.1. Đặt breakpoint tại dòng lệnh cần kiểm tra
- 4.2. Chương trình tạm dừng → quan sát trạng thái
- 4.3. Kết hợp với in giá trị trung gian → hiệu quả nhất
V. Bảng sai lầm thường gặp
| Sai lầm | Đính chính |
|---|---|
| Chương trình chạy không báo lỗi → kết luận đúng | Không có lỗi ngoại lệ không có nghĩa là không có bug. Luôn cần kiểm thử với nhiều bộ dữ liệu |
| Chỉ test với 1–2 bộ dữ liệu rồi kết luận chương trình đúng | Cần test đa dạng: dữ liệu thông thường, ngẫu nhiên, và đặc biệt là vùng biên |
| Quên kiểm tra dữ liệu vùng biên | Lỗi thường phát sinh ở các giá trị cực trị ($0$, giá trị rất lớn, danh sách rỗng,…) |
Dùng input() mà quên chuyển kiểu bằng int() hoặc float() |
input() luôn trả về xâu kí tự, phép + sẽ nối xâu thay vì cộng số |
Nhầm j > 1 với j > 0 trong thuật toán sắp xếp |
Điều kiện j > 1 khiến phần tử ở đầu danh sách không bao giờ được so sánh và hoán đổi |
| Chỉ dựa vào test mà không đọc lại mã nguồn | Nên kết hợp nhiều phương pháp: đọc mã, test dữ liệu, in trung gian, breakpoint |

Thầy Phạm Thành Danh
(Người kiểm duyệt, ra đề)
Chức vụ: Trưởng ban biên soạn môn Tin Học THPT
Trình độ: Cử nhân Sư phạm Tin học, Chứng chỉ hạng II, Chứng chỉ Tin học, Ngoại ngữ B1
Kinh nghiệm: 8+ năm kinh nghiệm tại Trường THPT Thuận Hóa
