AILapTrinh.comAI Lập Trình
Đọc hiểu và cải tạo mã cũ

Kiểm thử đặc tả hành vi cho mã cũ: chụp lại cách hệ thống đang chạy trước khi sửa

Characterization test là gì, vì sao mã cũ cần nó trước mọi lần sửa, cách dùng AI sinh ca đầu vào, ghi kết quả thật làm chuẩn và xử lý hành vi trông như lỗi.

Wide shot of a quiet open office in Bac Ninh at dusk, a lone developer seen from behind at a desk with two blank monitors, rows of empty chairs, cool blue light

Kiểm thử đặc tả hành vi (characterization test) là kiểm thử ghi lại hệ thống hiện đang làm gì, chứ không phải hệ thống lẽ ra phải làm gì. Bạn gọi đoạn mã cũ với nhiều đầu vào, ghi lại đầu ra thật, rồi biến các cặp vào ra đó thành kiểm thử. Từ lúc đó, mọi lần sửa mà làm đầu ra thay đổi sẽ bị phát hiện ngay. Với mã cũ không tài liệu, không kiểm thử, đây là bước đầu tiên để dám sửa.

Thuật ngữ này được Michael Feathers phổ biến trong cuốn Working Effectively with Legacy Code xuất bản năm 2004, cuốn sách định nghĩa mã cũ một cách thẳng thắn là mã không có kiểm thử. AI giúp làm loại kiểm thử này nhanh hơn nhiều: nghĩ ra ca đầu vào, viết khung kiểm thử, dựng dữ liệu giả. Nhưng có một nguyên tắc không đổi: kết quả mong đợi phải lấy từ máy chạy thật, không bao giờ để AI tự đoán.

Khác gì kiểm thử đơn vị thông thường

Kiểm thử đơn vị thông thường bắt đầu từ yêu cầu: hàm tính phí giao hàng phải trả 0 cho đơn trên một mức nào đó, nên bạn viết kiểm thử khẳng định điều đó. Kiểm thử đặc tả hành vi đi theo chiều ngược lại. Bạn không biết chắc yêu cầu ban đầu là gì, tài liệu không còn, người viết đã nghỉ. Thứ duy nhất bạn chắc là hệ thống đang chạy trên máy chủ thật và khách hàng đang quen với cách nó chạy. Vì thế bạn ghi lại đúng cách nó chạy, kể cả những chỗ trông kỳ lạ.

Sự khác biệt này quyết định cách làm việc với AI. Khi viết kiểm thử đơn vị thông thường, bạn có thể bảo AI viết kiểm thử theo yêu cầu. Khi viết kiểm thử đặc tả hành vi, nếu bạn bảo AI "viết kiểm thử cho hàm này", nó sẽ đọc mã, đoán hàm lẽ ra phải làm gì, rồi viết khẳng định theo phỏng đoán đó. Kiểm thử ấy có thể đỏ ngay lần chạy đầu vì AI đoán khác thực tế, hoặc tệ hơn, xanh nhưng khẳng định sai thứ. Danh mục kiểm thử của trang bàn sâu về kiểm thử đơn vị nói chung; bài này chỉ tập trung vào tình huống mã cũ chưa có lưới an toàn.

Quy trình năm bước với AI

Quy trình dưới đây áp dụng cho một hàm hoặc một lớp có đầu vào, đầu ra xác định được. Phần sau bàn về trường hợp có cơ sở dữ liệu và dịch vụ bên ngoài.

  1. Xác định ranh giới. Chọn điểm gọi hẹp nhất bao trọn vùng sắp sửa: một hàm công khai, một phương thức của lớp, một địa chỉ web. Ghi rõ đầu vào gồm những gì, đầu ra gồm những gì, kể cả tác dụng phụ như ghi bảng hay gửi thư.
  2. Nhờ AI liệt kê ca đầu vào. Câu lệnh mẫu: Đọc hàm dưới đây và liệt kê các bộ đầu vào để đi qua mọi nhánh điều kiện, kèm các giá trị biên: rỗng, không, số âm, chuỗi rất dài, ký tự tiếng Việt có dấu. Không ghi kết quả mong đợi.
  3. Chạy thật để lấy đầu ra. Viết một tập lệnh nhỏ gọi hàm với từng bộ đầu vào và in kết quả. Đầu ra này là "chuẩn" của bạn.
  4. Biến thành kiểm thử. Nhờ AI chuyển danh sách cặp vào ra thành kiểm thử theo khung của dự án, ví dụ PHPUnit, pytest hay Jest, với kết quả mong đợi dán nguyên từ bước 3.
  5. Kiểm độ phủ. Chạy kiểm thử kèm công cụ đo độ phủ để xem nhánh nào chưa được chạm tới, rồi quay lại bước 2 bổ sung ca cho nhánh đó.

Bước 3 là bước không được nhờ AI làm thay. Nếu AI tự ghi kết quả mong đợi, kiểm thử của bạn đặc tả trí tưởng tượng của AI chứ không phải hệ thống.

Kỹ thuật bản chụp vàng cho đầu ra lớn

Có những đoạn mã cũ đầu ra rất lớn: một trang HTML hoàn chỉnh, một tệp báo cáo, một chuỗi JSON nhiều tầng. Viết khẳng định cho từng trường là không khả thi. Kỹ thuật thường gọi là bản chụp vàng (golden master) hay kiểm thử phê duyệt (approval testing) giải quyết việc này: chạy mã với một bộ đầu vào cố định, lưu toàn bộ đầu ra vào tệp, và kiểm thử chỉ đơn giản so đầu ra mới với tệp đã lưu. Nhiều khung kiểm thử có sẵn chức năng chụp nhanh (snapshot) làm đúng việc này.

AI giúp ở hai chỗ. Thứ nhất, sinh nhiều bộ đầu vào đa dạng để bản chụp phủ được nhiều nhánh. Thứ hai, khi bản chụp khác đi sau một lần sửa, bạn đưa phần khác biệt cho AI và hỏi: khác biệt này là do thay đổi có chủ ý hay là tác dụng phụ ngoài ý muốn. Câu hỏi này đặc biệt hữu ích khi khác biệt dài và rải rác.

Bản chụp có một điểm yếu cần xử lý trước: dữ liệu thay đổi mỗi lần chạy như thời gian hiện tại, số ngẫu nhiên, mã định danh tự sinh. Nếu không cố định chúng, bản chụp sẽ đỏ mọi lần chạy và nhóm sẽ quen bấm cập nhật mà không đọc. Hỏi AI tìm mọi chỗ trong vùng mã có dùng thời gian hiện tại hoặc số ngẫu nhiên, rồi tìm cách truyền chúng vào từ ngoài hoặc thay bằng giá trị cố định khi chạy kiểm thử.

Khi mã cũ dính cơ sở dữ liệu và dịch vụ bên ngoài

Dùng cơ sở dữ liệu thử riêng. Dựng một cơ sở dữ liệu thử với cấu trúc giống thật và dữ liệu giả, nạp lại trạng thái đầu trước mỗi lần chạy. Không bao giờ chạy kiểm thử ghi dữ liệu lên cơ sở dữ liệu thật.

Ghi lại trạng thái bảng sau khi chạy. Với hàm có tác dụng ghi bảng, đầu ra cần chụp gồm cả các dòng bị thêm hoặc sửa. AI viết nhanh truy vấn lấy những dòng đó để đưa vào bản chụp.

Thay dịch vụ ngoài bằng bản giả. Gửi thư, gọi cổng thanh toán, gọi dịch vụ khác nên được thay bằng một bản giả ghi lại lời gọi. Kiểm thử khẳng định trên nội dung lời gọi, không gửi gì ra ngoài thật.

Tạo đường nối nếu chưa có. Mã cũ thường gọi thẳng hàm gửi thư ở giữa hàm nghiệp vụ, không có chỗ để thay bản giả. Feathers gọi chỗ có thể thay hành vi mà không sửa nơi gọi là đường nối. Tạo đường nối nhỏ nhất có thể, như tách lời gọi ra một hàm truyền vào được, là một thay đổi tối thiểu nên làm cẩn thận và có người rà.

Che dữ liệu khi lấy mẫu từ hệ thống thật. Nếu cần dữ liệu giống thật để ca thử sát thực tế, lấy mẫu rồi thay toàn bộ tên, email, số điện thoại, địa chỉ bằng giá trị giả trước khi dùng hoặc đưa cho AI.

Gặp hành vi trông như lỗi: ghi lại, đừng sửa ngay

Khi chạy thật, bạn sẽ gặp đầu ra kỳ lạ. Phản xạ tự nhiên là sửa luôn. Bảng dưới là cách xử lý đúng thứ tự.

Tình huốngViệc làm ngayViệc làm sau
Hàm trả kết quả rõ ràng sai với một đầu vào biênGhi kiểm thử khẳng định đúng kết quả sai đó, đặt tên nói rõ là hành vi hiện tạiBáo lỗi riêng, bàn với người hiểu nghiệp vụ, sửa trong commit riêng và đổi kiểm thử
Hàm ném lỗi với đầu vào rỗngGhi kiểm thử khẳng định có lỗi ném raXem nơi gọi có dựa vào việc ném lỗi đó không
Hai lần chạy cùng đầu vào cho kết quả khác nhauKhông ghi kiểm thử vội, tìm nguồn không xác địnhCố định thời gian, số ngẫu nhiên, thứ tự sắp xếp rồi mới chụp
Kết quả phụ thuộc cấu hình máyGhi rõ cấu hình dùng khi chụpĐưa cấu hình đó vào môi trường chạy kiểm thử

Lý do không sửa ngay rất thực tế: có thể một hệ thống khác, một báo cáo, hay thói quen của người dùng đang dựa vào chính hành vi sai đó. Sửa lẫn vào lúc đang dựng lưới an toàn khiến bạn mất luôn mốc so sánh. Ghi lại trước, sửa có chủ đích sau.

Ví dụ giả định: dựng lưới cho hàm xuất hoá đơn

Giả sử bạn cần sửa hàm tạo nội dung hoá đơn của một ứng dụng bán hàng nhỏ: nhận mã đơn, đọc các dòng hàng, tính chiết khấu, thuế, làm tròn, trả về một chuỗi HTML. Không có kiểm thử nào, và chỉ một sai lệch nhỏ ở phần làm tròn cũng đủ khiến khách hàng phàn nàn.

Bạn đưa hàm cho AI và nhận về hai mươi bộ đầu vào gợi ý: đơn một dòng, đơn nhiều dòng, đơn có chiết khấu phần trăm, đơn có chiết khấu số tiền cố định lớn hơn tổng tiền, số lượng không, đơn giá có phần lẻ, tên sản phẩm tiếng Việt có dấu và có ký tự đặc biệt. Bạn tạo các đơn đó trong cơ sở dữ liệu thử, chạy hàm, lưu hai mươi tệp HTML làm bản chụp. Trong lúc chạy, bạn phát hiện đơn có chiết khấu lớn hơn tổng tiền cho ra số tiền âm. Bạn không sửa, chỉ ghi chú vào tên kiểm thử và mở một phiếu lỗi riêng.

Công cụ đo độ phủ cho thấy một nhánh xử lý khách miễn thuế chưa ai chạm tới. Bạn hỏi AI điều kiện để vào nhánh đó, thêm hai ca, chụp thêm. Đến đây lưới đã đủ: bạn bắt đầu tái cấu trúc phần tính tiền, và mỗi bước đều được hai mươi hai bản chụp canh giữ.

Giữ hay bỏ các kiểm thử này về lâu dài

Kiểm thử đặc tả hành vi là giàn giáo. Nó giúp bạn sửa an toàn trong giai đoạn đầu, nhưng một phần sẽ trở nên vướng víu: những kiểm thử khẳng định hành vi lỗi đã được sửa, những bản chụp quá lớn mà mỗi thay đổi giao diện nhỏ lại phải cập nhật. Khi vùng mã đã được tái cấu trúc và có kiểm thử đơn vị viết theo yêu cầu rõ ràng, bạn có thể bỏ dần kiểm thử đặc tả tương ứng.

Có hai thói quen giúp bộ kiểm thử không mục ruỗng. Một là đặt tên kiểm thử nói rõ đây là đặc tả hành vi hiện tại, kèm ngày, để người sau biết nó không phải yêu cầu nghiệp vụ. Hai là không bao giờ cập nhật bản chụp hàng loạt mà không đọc khác biệt; nếu khác biệt quá dài để đọc, đó là dấu hiệu thay đổi quá lớn cho một lần. AI có thể giúp tóm tắt khác biệt, nhưng quyết định chấp nhận vẫn là của người.

Câu hỏi thường gặp

Characterization test khác gì regression test?

Kiểm thử hồi quy là mục đích chung: phát hiện khi thay đổi làm hỏng thứ đang chạy. Kiểm thử đặc tả hành vi là một cách xây bộ kiểm thử hồi quy cho mã chưa có kiểm thử, bằng cách ghi lại hành vi hiện tại thay vì viết theo yêu cầu.

Có thể để AI tự viết cả kiểm thử lẫn kết quả mong đợi không?

Không nên với mã cũ. AI đoán kết quả từ việc đọc mã, có thể sai với thực tế. Kết quả mong đợi phải lấy từ việc chạy thật trên môi trường giống máy chủ. AI chỉ nên sinh ca đầu vào và khung kiểm thử.

Cần độ phủ bao nhiêu phần trăm mới bắt đầu sửa?

Không có con số chung. Điều cần là mọi nhánh trong vùng bạn sắp sửa đã có ít nhất một ca chạy qua. Công cụ đo độ phủ cho biết nhánh nào chưa chạm tới; phần mã ngoài vùng sửa không cần phủ ngay.

Mã cũ không tách được hàm để gọi riêng thì làm sao?

Kiểm thử ở mức cao hơn: gọi địa chỉ web hoặc chạy tập lệnh dòng lệnh rồi so đầu ra và trạng thái cơ sở dữ liệu. Chậm hơn nhưng không cần sửa mã. Sau khi có lưới đó, bạn tách dần để kiểm thử nhỏ hơn.

Bản chụp vàng có làm kiểm thử dễ vỡ không?

Có nếu đầu ra chứa thời gian, số ngẫu nhiên hoặc chi tiết hiển thị hay đổi. Cố định các giá trị đó khi chạy kiểm thử và chỉ chụp phần có ý nghĩa. Bản chụp vỡ liên tục sẽ khiến nhóm cập nhật mà không đọc, làm mất tác dụng.

Cần tư vấn cụ thể cho trường hợp của bạn?

Chúng tôi sẽ liên hệ trong vòng 24 giờ.

Đăng ký tư vấn ngay

Bài viết liên quan

Bạn cần hỗ trợ thêm?

Để lại thông tin, chúng tôi sẽ liên hệ trong 24 giờ.

hoặc
Gọi ngay 0926 138 138