Cách kiểm thử mã gửi SMS an toàn trong CI

Cách kiểm thử mã gửi SMS an toàn trong CI

Kiểm thử mã gửi SMS nên chia thành bốn lớp: kiểm tra hàm thuần, mô phỏng ở ranh giới HTTP, kiểm tra hợp đồng phản hồi và một nhóm smoke test gửi thật với phạm vi hẹp. Cách này giúp phát hiện lỗi chia đoạn, sai mẫu, phân tích phản hồi và xử lý timeout mà không biến mỗi lần chạy CI thành một lần gửi tin tốn phí.

Kỹ sư phần mềm xem sơ đồ bốn lớp kiểm thử mã gửi SMS

Vì sao kiểm thử mã gửi SMS khó hơn API thông thường?

SMS có tác dụng phụ khó hoàn tác. Tin đã gửi không thể rollback, còn một vòng lặp lỗi có thể gửi nhầm cho khách hàng thật. Chi phí cũng thường phát sinh lúc yêu cầu được nộp cho nhà mạng, không phải sau khi người nhận mở tin, nên số lượng HTTP request không phản ánh chính xác số tiền đã tiêu.

Một request còn có thể chứa nhiều người nhận. Tin Unicode thường chứa ít ký tự hơn mỗi phân đoạn so với tin Latin, vì vậy nội dung tiếng Việt dài có thể làm tăng số phân đoạn dù ứng dụng chỉ gọi API một lần. Lỗi xác thực cũng có mức độ rủi ro khác lỗi thông báo: OTP gửi trùng có thể khiến người dùng bị khóa hoặc nhận nhiều mã, trong khi một tin quảng bá trễ thường chỉ ảnh hưởng đến lịch gửi.

Phần khó nhất nằm ngoài mã nguồn: nhà mạng có thể lọc nội dung, thiết bị hiển thị khác nhau và trạng thái giao hàng có thể đến muộn. Đội phát triển không thể unit test hành vi của mọi nhà mạng, nhưng có thể kiểm tra chính xác mẫu đã duyệt, dữ liệu gửi đi và cách hệ thống xử lý từng trạng thái.

Cách xây dựng bốn lớp kiểm thử SMS

Lớp 1: kiểm tra hàm thuần

Đặt các phép tính và biến đổi dễ sai vào hàm không gọi mạng. Kiểm tra số phân đoạn, cách thay biến trong mẫu, chuẩn hóa số điện thoại, mã hóa Unicode và dữ liệu rút gọn trong nhật ký. Những ca này chạy nhanh, nên dùng nhiều trường hợp biên thay vì chỉ kiểm tra một tin nhắn thành công.

  • Tin rỗng hoặc vượt giới hạn ký tự.
  • Ký tự tiếng Việt, emoji và dấu xuống dòng.
  • Nhiều người nhận, số trùng hoặc số sai định dạng.
  • Biến mẫu bị thiếu, thừa hoặc chứa dữ liệu cần che.

Lớp 2: mô phỏng tại ranh giới vận chuyển

Mock ở lớp HTTP, không mock chính lớp SmsClient mà ứng dụng đang kiểm tra. Nếu mock wrapper, test vẫn có thể xanh dù mã tạo sai URL, header, phương thức hoặc thân request. Hãy để wrapper chạy thật, sau đó thay máy khách HTTP bằng một máy chủ mô phỏng và kiểm tra request thực tế.

Trong Python, đội ngũ thường dùng respx hoặc responses; Node.js có nock; PHP có Guzzle MockHandler kết hợp middleware lưu lịch sử. Tên thư viện chỉ là ví dụ, còn nguyên tắc vẫn giữ nguyên: fixture phải kiểm tra request mà ứng dụng thật sự gửi.

Lớp 3: kiểm tra hợp đồng phản hồi

Lưu các phản hồi thực tế làm fixture, rồi kiểm tra parser với trạng thái thành công, lỗi một phần và trường bị thiếu. Đừng tự gõ JSON theo trí nhớ. Nếu tài liệu hoặc endpoint thực tế có hai kiểu dữ liệu cho cùng trường, parser cần xử lý cả hai và test phải ghi rõ đó là khác biệt đã quan sát, không phải quy ước tự suy đoán.

Với webhook báo cáo giao hàng, hãy lưu payload thô trước khi phân tích. Kiểm tra cả thân form-encoded nếu đó là định dạng hệ thống nhận được, thay vì mặc định JSON. Schema chưa được nhà cung cấp công bố không nên gọi là “hợp đồng”; chỉ nên gắn nhãn fixture là dữ liệu đã quan sát trong tài khoản kiểm thử.

Lớp 4: smoke test gửi thật

Chỉ giữ một nhóm nhỏ ca gửi thật, dùng số kiểm soát và nội dung vô hại. Nếu nhà cung cấp có cờ kiểm thử hoặc sandbox, đọc đúng tài liệu để biết tin có được giao hay không; đừng suy ra rằng “không giao” đồng nghĩa với “không tính phí”. Ghi số dư trước và sau một lần chạy có kiểm soát nếu cần xác nhận hành vi tài khoản.

Smoke test nên chạy ít lần, có giới hạn số người nhận và có khóa chống chạy song song. Tuyệt đối không dùng số khách hàng trong test. Nhật ký chỉ lưu mã chạy, trạng thái và giá trị đã che, không ghi toàn bộ OTP.

Kiểm thử timeout, retry và giới hạn gửi

Timeout kết nối khác timeout đọc dữ liệu. Timeout kết nối thường cho biết request chưa đến nơi; timeout đọc chỉ cho biết ứng dụng chưa biết kết quả. Ở tình huống thứ hai, retry mù có thể gửi trùng tin và tính phí hai lần, vì vậy state machine nên có trạng thái “chưa xác định” thay vì lập tức quay về “chờ gửi”.

Đưa đồng hồ vào tham số của bộ giới hạn tốc độ và lịch backoff. Test sẽ không phải chờ thời gian thật, đồng thời có thể kiểm tra chính xác lần gửi lại, khoảng cách giữa các lần thử và giới hạn theo từng người dùng. Thêm ca chạy đồng thời để xác nhận cùng một sự kiện không tạo ra hai bản ghi gửi.

Phần tiêu cực cần có ít nhất: số không hợp lệ, phản hồi chậm, nhà cung cấp từ chối, mã hết hạn, gửi lại làm vô hiệu mã cũ, lỗi tạm thời và webhook đến ngoài thứ tự. Đây là các kịch bản thường bị bỏ qua khi suite chỉ kiểm tra đường thành công.

Hướng dẫn đưa kiểm thử SMS vào CI

Mỗi pull request chỉ cần chạy bộ smoke ngắn và các test hàm thuần. Lịch chạy ban đêm có thể mở rộng theo mẫu, khu vực và nhà cung cấp, nhưng phải giới hạn ngân sách cũng như số lần gửi thật. Khi test lỗi, lưu artifact đã khử danh tính để người xử lý thấy request, trạng thái parser, thời gian chờ và mã chạy mà không thấy OTP đầy đủ.

Nếu hệ thống có bước kiểm tra mẫu SMS, chạy bước đó trước khi merge. Một thay đổi nhỏ trong nội dung có thể làm tin bị từ chối hoặc làm lệch biến động, dù test tích hợp vẫn gọi API thành công. So sánh danh sách mã lỗi mà ứng dụng xử lý với danh sách được công bố theo lịch sẽ giúp phát hiện mã mới trước khi người dùng gặp lỗi.

Checklist trước khi merge

  • Hàm tính phân đoạn và dựng mẫu có test ký tự biên.
  • Wrapper HTTP được kiểm tra bằng request thật đến máy mô phỏng.
  • Fixture phản hồi được lấy từ dữ liệu quan sát, có ca lỗi một phần.
  • Timeout đọc, retry và trạng thái chưa xác định đã được kiểm tra.
  • Webhook được lưu payload thô trước khi parse.
  • Smoke test dùng số kiểm soát, có giới hạn và không chạy song song ngoài ý muốn.
  • Log, screenshot và artifact không chứa OTP hoặc thông tin cá nhân thật.

Liên quan: kiểm thử SMS OTP trong CI, checklist QA cho SMS OTPrủi ro khi dùng SMS OTP cho MFA.

Thuê SIM nhận OTP từ 500đ
Dùng thử