Phần lớn lỗi lọt qua kiểm thử không nằm ở đường đi bình thường mà nằm ở mép: chuỗi rỗng, số 0, ngày cuối tháng, khách có tên một chữ, số tiền chia không hết. Mã do AI viết đặc biệt hay gãy ở những chỗ này, vì nó học từ hàng triệu đoạn mã xử lý trường hợp phổ biến và thường viết đẹp cho đúng trường hợp đó. Tin tốt là AI cũng rất giỏi liệt kê ca biên, nếu bạn hỏi đúng cách và hỏi trước khi nó viết mã.
Bài này đưa ra bảng ca biên theo kiểu dữ liệu, câu lệnh để AI liệt kê mà không vội viết mã, những mép riêng của dữ liệu tiếng Việt, một ví dụ giả định về chia tiền bị lệch một đồng, và kiểm thử theo thuộc tính khi ca biên nhiều tới mức không kể hết được.
Ca biên là gì và vì sao mã AI viết hay gãy ở đó
Ca biên là những đầu vào nằm ở giới hạn của miền giá trị hợp lệ hoặc ngay bên ngoài nó: giá trị nhỏ nhất, lớn nhất, rỗng, thiếu, trùng, đúng tại mốc chuyển quy tắc. Lỗi tập trung ở đây vì người viết mã, kể cả AI, hình dung bài toán qua vài ví dụ điển hình. Một vòng lặp chạy đúng với danh sách mười phần tử vẫn có thể sai với danh sách một phần tử hoặc không phần tử nào.
Mã AI sinh ra có thêm một đặc điểm: nó trông gọn và tự tin. Khi đọc một hàm chừng mươi dòng viết sạch sẽ, mắt người dễ bỏ qua câu hỏi "nếu danh sách rỗng thì sao". Ngoài ra AI hay mặc định điều kiện của nơi nó học nhiều nhất: định dạng ngày kiểu Mỹ, dấu chấm thập phân, tên người có họ và tên riêng, văn bản không dấu. Với dữ liệu của người dùng Việt Nam, những mặc định này gãy thường xuyên hơn bạn nghĩ.
Vì vậy thứ tự đúng là: trước khi nhận mã, bắt AI liệt kê ca biên; bạn duyệt danh sách; sau đó mới cho viết mã và kiểm thử cùng lúc. Liệt kê sau khi đã có mã thì AI dễ chỉ chọn những ca mà mã hiện tại xử lý được.
Bảng ca biên theo kiểu dữ liệu
Bảng sau là điểm xuất phát để bạn dán vào câu lệnh hoặc tự soát. Không phải hàm nào cũng cần mọi dòng; chọn theo kiểu dữ liệu hàm nhận vào.
| Kiểu dữ liệu | Ca biên nên nghĩ tới | Lỗi hay gặp |
|---|---|---|
| Số nguyên, số thực | 0, số âm, giá trị lớn nhất cho phép, ngay trên và dưới mốc, số thực có phần lẻ rất nhỏ | Chia cho 0, so sánh số thực bằng dấu bằng, tràn số ở ngôn ngữ có giới hạn kiểu |
| Chuỗi | Rỗng, chỉ có khoảng trắng, rất dài, có ký tự đặc biệt, có biểu tượng cảm xúc, có dấu tiếng Việt | Cắt chuỗi giữa một ký tự nhiều byte, đếm độ dài sai |
| Danh sách, mảng | Rỗng, một phần tử, phần tử trùng, đã sắp xếp sẵn, sắp ngược, rất nhiều phần tử | Lấy phần tử đầu khi danh sách rỗng, vòng lặp lệch một vị trí |
| Ngày giờ | Ngày 29 tháng 2, cuối tháng, cuối năm, nửa đêm, múi giờ khác, giờ máy chủ khác giờ người dùng | Cộng tháng ra ngày không tồn tại, lưu giờ địa phương thay vì giờ chuẩn |
| Tiền | Số tiền 0, chia không hết, cộng nhiều khoản nhỏ, giảm giá lớn hơn giá | Dùng số thực cho tiền, làm tròn từng phần rồi cộng ra lệch tổng |
| Dữ liệu tuỳ chọn | Trường thiếu hẳn, có mà giá trị rỗng, có mà giá trị null | Coi ba trường hợp này là một, hoặc ngược lại, xử lý lẫn lộn |
Dòng cuối của bảng hay bị coi nhẹ. Trong một biểu mẫu, "không nhập" và "nhập rồi xoá" có thể gửi lên hai giá trị khác nhau; nếu hàm của bạn coi chúng khác nhau thì phải có ca cho cả hai.
Câu lệnh để AI liệt kê ca biên trước khi viết mã
Điều quan trọng là tách việc liệt kê ra khỏi việc viết mã. Một mẫu câu lệnh dùng được ngay:
"Đây là mô tả hàm [mô tả, kiểu dữ liệu vào ra, quy tắc nghiệp vụ]. Dữ liệu do người dùng Việt Nam nhập qua biểu mẫu trên điện thoại. Chưa viết mã. Liệt kê các ca biên và đầu vào bất thường mà hàm phải xử lý, chia nhóm theo: giới hạn số, chuỗi và tiếng Việt, rỗng và thiếu, ngày giờ, dữ liệu sai định dạng. Với mỗi ca, ghi đầu vào cụ thể và đề xuất hành vi mong đợi, đánh dấu những ca mà mô tả của hàm chưa nói rõ."
Phần đánh dấu ca chưa rõ là chỗ đáng tiền nhất. AI sẽ chỉ ra những câu hỏi bạn chưa trả lời, chẳng hạn giảm giá lớn hơn giá thì trả về 0 hay báo lỗi. Những câu này cần người hiểu nghiệp vụ quyết, có khi phải hỏi lại người đặt yêu cầu, và quyết xong thì ghi vào đặc tả.
Khi nhận danh sách, bạn gạch những ca không áp dụng, thêm ca riêng của hệ thống mình mà AI không thể biết, ví dụ một mã sản phẩm cũ có ký tự đặc biệt vẫn còn trong kho. Sau đó mới giao viết mã kèm kiểm thử cho đúng danh sách đã duyệt.
Những mép riêng của dữ liệu tiếng Việt
Hai dạng Unicode của cùng một chữ. Chữ có dấu có thể lưu ở dạng dựng sẵn (một ký tự) hoặc tổ hợp (chữ gốc cộng dấu). Trên màn hình giống hệt nhau nhưng so sánh chuỗi ra khác, tìm kiếm không thấy, đếm độ dài lệch. Luôn có ca cho đầu vào dạng tổ hợp và chuẩn hoá về một dạng ngay từ cửa vào.
Sắp xếp theo bảng chữ cái tiếng Việt. Sắp xếp theo mã ký tự đặt chữ đ ra sau chữ z và xếp chữ có dấu lung tung. Nếu tính năng có danh sách tên cần sắp xếp, thêm ca kiểm thứ tự của các tên bắt đầu bằng d và đ, a và â.
Họ tên không theo khuôn. Có người tên một chữ, có người họ kép, có người dùng tên nước ngoài. Hàm tách họ và tên nếu giả định luôn có ít nhất hai từ sẽ gãy. Thêm ca tên một từ và tên rất dài.
Số và tiền theo thói quen Việt. Người dùng gõ 1.500.000 với dấu chấm phân cách hàng nghìn, trong khi mã mặc định coi dấu chấm là thập phân. Thêm ca có dấu chấm, dấu phẩy, có chữ đ hoặc chữ "k" phía sau.
Ngày tháng và múi giờ. Người Việt viết ngày trước tháng; máy chủ có thể chạy giờ chuẩn quốc tế trong khi người dùng ở múi giờ cộng bảy. Đơn đặt lúc 23 giờ 30 có thể rơi vào ngày hôm sau trong báo cáo. Thêm ca sát nửa đêm.
Địa chỉ hành chính đã đổi. Từ giữa năm 2025, cả nước còn 34 tỉnh, thành phố và bỏ cấp huyện. Dữ liệu cũ vẫn mang tên đơn vị cũ. Hàm xử lý địa chỉ cần ca cho cả hai kiểu.
Ví dụ giả định: chia tiền bị lệch một đồng
Ứng dụng chia hoá đơn cho một nhóm người. Bạn nhờ AI viết hàm chia tổng tiền thành số phần bằng nhau. Bản đầu tiên AI đưa ra làm tròn thương số rồi nhân bản cho mỗi người. Với 100.000 đồng chia ba người, mỗi người 33.333 đồng, cộng lại thiếu một đồng. Kiểm thử AI viết kèm chỉ kiểm một phần tử bằng 33.333 nên vẫn xanh. Một đồng nghe nhỏ, nhưng trong hệ thống đối soát, tổng không khớp là cả quy trình dừng lại.
Bản sửa giữ đúng tổng bằng cách phân phần dư cho vài người đầu (đơn vị tính là đồng, kiểu số nguyên):
def chia_deu(tong, so_phan):
if so_phan <= 0:
raise ValueError("so_phan phải lớn hơn 0")
phan, du = divmod(tong, so_phan)
return [phan + 1 if i < du else phan for i in range(so_phan)]
Ca biên đáng kiểm: chia hết, chia dư, tổng nhỏ hơn số phần (2 đồng chia năm người ra hai người 1 đồng, ba người 0 đồng), tổng bằng 0, số phần bằng 0 phải báo lỗi. Còn tổng âm, chẳng hạn khi hoàn tiền, thì mô tả ban đầu chưa nói; đó đúng là loại câu hỏi cần đưa về cho người hiểu nghiệp vụ quyết định chứ không để AI tự chọn.
Khi ca biên quá nhiều: kiểm thử theo thuộc tính
Với những hàm có miền đầu vào rộng, liệt kê từng ca không bao giờ đủ. Kiểm thử theo thuộc tính đổi cách nghĩ: thay vì viết "100 chia 3 ra 34, 33, 33", bạn mô tả tính chất luôn đúng, rồi để thư viện tự sinh hàng trăm đầu vào, kể cả đầu vào ở mép, để tìm ca phá vỡ. Python có Hypothesis, JavaScript có fast-check; bạn kiểm tài liệu của bản đang dùng trước khi chép mẫu.
from hypothesis import given, strategies as st
@given(st.integers(min_value=0, max_value=10**9),
st.integers(min_value=1, max_value=1000))
def test_chia_deu_giu_dung_tong(tong, so_phan):
ket_qua = chia_deu(tong, so_phan)
assert len(ket_qua) == so_phan
assert sum(ket_qua) == tong
assert max(ket_qua) - min(ket_qua) <= 1
Ba câu kiểm là ba thuộc tính: đủ số phần, giữ đúng tổng, các phần chênh nhau không quá một đồng. Bản làm tròn đầu tiên sẽ bị bắt ngay ở thuộc tính thứ hai. AI khá giỏi gợi ý thuộc tính nếu bạn hỏi "hàm này có những tính chất nào luôn đúng với mọi đầu vào hợp lệ", nhưng bạn phải tự xác nhận từng tính chất có đúng với nghiệp vụ hay không, vì một thuộc tính sai sẽ làm mọi kiểm thử phía sau mất nghĩa.
Biết dừng: ưu tiên ca biên theo rủi ro
Danh sách ca biên AI đưa ra có thể dài vài chục dòng. Viết hết thì bộ kiểm thử nặng nề mà nhiều ca chẳng bao giờ xảy ra. Cách chọn:
- Ưu tiên ca dính tới tiền, quyền truy cập và dữ liệu không lấy lại được; một lỗi ở đây đắt hơn mười lỗi hiển thị.
- Ưu tiên ca từng gây lỗi trong hệ thống của bạn hoặc hệ thống tương tự; lịch sử lỗi là bản đồ rủi ro tốt nhất.
- Gộp các ca cùng nhánh xử lý: nếu chuỗi rỗng và chuỗi chỉ có khoảng trắng đi cùng một nhánh sau khi cắt khoảng trắng, một ca đại diện là đủ, hoặc dùng bảng tham số.
- Ca mà kiểu dữ liệu đã chặn sẵn, ví dụ ngôn ngữ có kiểu tĩnh không cho truyền null, thì không cần kiểm lại.
- Ca liên quan tới đầu vào từ bên ngoài như biểu mẫu, tệp tải lên, dữ liệu từ dịch vụ khác thì nên kiểm kỹ hơn ca chỉ do mã nội bộ gọi.
Mỗi ca biên bạn quyết định bỏ qua, ghi một dòng lý do trong mô tả commit hoặc ghi chú kiểm thử. Lần sau có lỗi đúng ở chỗ đó, nhóm biết đó là rủi ro đã chấp nhận chứ không phải sơ suất.
Câu hỏi thường gặp
Ca biên khác ca lỗi đầu vào thế nào?
Ca biên là đầu vào hợp lệ nằm ở giới hạn, như số tiền bằng 0 hay danh sách một phần tử. Ca lỗi đầu vào là dữ liệu không hợp lệ, như chữ trong ô số. Cả hai đều cần kiểm, nhưng hành vi mong đợi khác nhau: một bên phải chạy đúng, một bên phải từ chối rõ ràng.
Có nên để AI tự viết kiểm thử ca biên luôn không?
Nên để AI liệt kê trước, bạn duyệt rồi mới cho viết. Nếu để AI vừa nghĩ ca vừa viết mã vừa viết kiểm thử trong một lượt, nó thường chọn những ca mà mã của chính nó đã xử lý được, và những mép quan trọng nhất bị bỏ sót.
Kiểm thử theo thuộc tính có thay được kiểm thử bằng ví dụ không?
Không thay mà bổ sung. Ví dụ cụ thể dễ đọc và ghi lại quy tắc nghiệp vụ rõ ràng; thuộc tính tìm ra những đầu vào lạ mà bạn không nghĩ tới. Dự án thường giữ vài ví dụ tiêu biểu cộng vài thuộc tính cho hàm có miền đầu vào rộng.
Vì sao không dùng số thực cho tiền?
Số thực nhị phân không biểu diễn chính xác nhiều số thập phân, nên cộng nhiều khoản nhỏ dễ ra kết quả lệch một chút. Với tiền đồng, dùng số nguyên là đơn giản nhất; với tiền có phần lẻ, dùng kiểu thập phân của ngôn ngữ hoặc lưu theo đơn vị nhỏ nhất.
Dữ liệu tiếng Việt dạng tổ hợp từ đâu ra?
Có thể đến từ một số bộ gõ, từ văn bản sao chép ở hệ điều hành hoặc ứng dụng khác, hay từ dữ liệu nhập khẩu từ hệ thống cũ. Bạn không kiểm soát được nguồn nhập, nên cách an toàn là chuẩn hoá về một dạng ngay khi nhận dữ liệu và có ca kiểm thử cho cả hai dạng.
Danh sách ca biên AI đưa có đáng tin hết không?
Không. AI có thể đưa ca không áp dụng cho hàm của bạn, đề xuất hành vi mong đợi sai nghiệp vụ, hoặc bỏ sót ca đặc thù của hệ thống. Coi danh sách là bản nháp để soát, người hiểu nghiệp vụ duyệt và bổ sung trước khi viết kiểm thử.