Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Test với data tiếng Việt — encoding & search

Software Testing Fundamentals Bài 52/60

Mở đầu — vì sao bài này quan trọng

Nếu bạn từng thấy một website hiển thị tên khách hàng thành "Nguyá»…n Vän An" thay vì "Nguyễn Văn An", hoặc gõ "hà nội" vào ô tìm kiếm mà kết quả trả về rỗng dù trong database rõ ràng có "Hà Nội", thì bạn đã chạm mặt đúng loại lỗi mà bài học này nói tới. Đây không phải lỗi hiếm — nó xuất hiện ở gần như mọi sản phẩm phần mềm phục vụ người dùng Việt Nam, và điều đáng sợ là nó thường lọt qua vòng test một cách êm ru vì đa số tester chỉ nhập "test", "abc", "123" khi kiểm thử.

Tiếng Việt là một trong những ngôn ngữ "khó chiều" nhất với máy tính: 134 ký tự có dấu, dấu thanh chồng lên dấu mũ (ế, ộ, ữ), và mỗi ký tự lại có thể được biểu diễn theo nhiều cách khác nhau ở tầng byte. Một tester người Việt có lợi thế cực lớn ở đây — bạn biết "Huế" khác "Hue", biết "gà" khác "ga" — nhưng lợi thế đó chỉ phát huy khi bạn hiểu rõ chuyện gì xảy ra bên dưới lớp giao diện. Bài này tập trung riêng vào hai trục: encoding (cách chữ tiếng Việt được lưu và truyền đi có bị "biến chất" không) và search (khả năng tìm kiếm đúng khi dữ liệu có dấu, không dấu, hoa thường lẫn lộn). Đây là kiến thức mà một QA làm sản phẩm cho thị trường Việt Nam bắt buộc phải nắm, vì nó là ranh giới giữa một tester "làm được việc" và một tester chỉ chạy theo kịch bản có sẵn.

Khái niệm cốt lõi

Encoding là gì và vì sao tiếng Việt hay vỡ

Máy tính không lưu chữ, nó lưu số (byte). Encoding là quy ước dịch từ chữ sang byte và ngược lại. Với tiếng Anh, mọi chuyện đơn giản vì bảng ASCII cũ chỉ cần 1 byte cho mỗi ký tự. Nhưng tiếng Việt có "ễ", "ợ", "ằ" — những ký tự nằm ngoài ASCII, nên cần chuẩn UTF-8, trong đó một ký tự tiếng Việt thường chiếm 2–3 byte.

Vấn đề xảy ra khi hai đầu của một đường ống dữ liệu dùng encoding khác nhau. Ví dụ kinh điển: chuỗi "Nguyễn" được ứng dụng gửi đi dưới dạng UTF-8, nhưng database lại được cấu hình để hiểu dữ liệu đến là Latin-1 (một encoding cũ của phương Tây). Kết quả là mỗi byte của "ễ" bị hiểu nhầm thành một ký tự Latin-1 riêng lẻ, và "Nguyễn" biến thành "Nguyá»…n". Hiện tượng byte bị diễn giải sai này dân trong nghề gọi là mojibake (tiếng Nhật, nghĩa là "chữ biến dạng").

Những điểm phổ biến nhất khiến tiếng Việt vỡ:

  • Database collation/charset sai: MySQL đặt cột là latin1 thay vì utf8mb4. Đây là thủ phạm số một.
  • Connection charset: DB đúng chuẩn utf8mb4 nhưng kết nối từ ứng dụng lại khai báo charset khác, dữ liệu vỡ ngay lúc ghi.
  • HTTP header và HTML meta: server không gửi Content-Type: charset=UTF-8, trình duyệt đoán sai.
  • File CSV/Excel import: Excel trên Windows hay lưu file dạng ANSI (Windows-1258), khi import lên hệ thống UTF-8 thì tên tiếng Việt vỡ hết.

utf8 vs utf8mb4 — cái bẫy riêng của MySQL

Có một chi tiết mà rất nhiều đội bỏ sót: trong MySQL, cái tên utf8 không phải là UTF-8 thật. Nó là utf8mb3, chỉ hỗ trợ tối đa 3 byte mỗi ký tự. Với chữ tiếng Việt thông thường thì vẫn ổn, nhưng nếu người dùng nhập emoji (4 byte) hay một số ký tự đặc biệt, dữ liệu sẽ bị cắt hoặc lỗi. Chuẩn đúng để dùng luôn là utf8mb4. Là tester, khi thấy schema khai báo CHARSET=utf8, bạn nên đặt câu hỏi ngay.

NFC vs NFD — hai chữ "ế" trông giống hệt nhau nhưng khác byte

Đây là phần tinh vi nhất và cũng dễ gây "bug ma" nhất. Unicode cho phép biểu diễn một ký tự có dấu theo hai cách:

  • NFC (Composed): "ế" là một code point duy nhất (U+1EBF). Đây là cách phổ biến và gọn.
  • NFD (Decomposed): "ế" được ghép từ chữ nền "e" + dấu mũ + dấu sắc, tức là nhiều code point rời rạc.
Trên màn hình, hai chuỗi này hiển thị y hệt nhau — mắt người không phân biệt được. Nhưng ở tầng byte chúng hoàn toàn khác, nên:

  • So sánh chuoiNFC == chuoiNFD sẽ trả về false.
  • Đếm độ dài chuỗi ra kết quả khác nhau (NFD dài hơn).
  • Tìm kiếm "Huế" (NFC) trong database lưu dạng NFD sẽ không ra kết quả.
Điều nguy hiểm: các thiết bị khác nhau tạo ra dạng khác nhau. Đáng chú ý nhất, macOS/iOS có xu hướng tạo tên file và chuỗi ở dạng NFD, trong khi Windows/Android và hầu hết bàn phím dùng NFC. Nghĩa là một người dùng iPhone gõ "Đà Nẵng" có thể tạo ra chuỗi byte khác với người dùng Android gõ cùng chữ đó. Giải pháp chuẩn là chuẩn hóa (normalize) toàn bộ dữ liệu về NFC trước khi lưu và trước khi so sánh.

Search tiếng Việt — dấu và hoa/thường

Người Việt có thói quen gõ không dấu khi tìm kiếm cho nhanh: gõ "ha noi" để tìm "Hà Nội", "dien thoai" để tìm "điện thoại". Một hệ thống search tốt cho thị trường Việt phải:

  • Bỏ qua dấu (accent-insensitive): "cà phê" tìm được cả khi gõ "ca phe".
  • Bỏ qua hoa/thường (case-insensitive): "SÀI GÒN" = "sài gòn".
  • Xử lý được chữ đặc thù: "đ" và "d" — nhiều người gõ "da nang" để tìm "Đà Nẵng".
Đây là điểm test cực kỳ quan trọng vì search là tính năng người dùng dùng hàng ngày, và một ô search "không hiểu tiếng Việt" khiến người dùng nghĩ sản phẩm bị hỏng.

Tình huống thực tế

Ví dụ 1 — Sàn thương mại điện tử và đơn hàng "Nguyá»…n"

Một sàn TMĐT quy mô vừa ở TP.HCM (giả định gọi là ShopViet) triển khai tính năng xuất hóa đơn PDF. Trong môi trường staging mọi thứ chạy tốt, nhưng khi lên production, bộ phận chăm sóc khách hàng bắt đầu nhận phàn nàn: hóa đơn in ra tên khách thành "Nguyá»…n Thị Lan". Điều tra ra, database chính dùng utf8mb4 chuẩn, nhưng thư viện sinh PDF đọc dữ liệu qua một connection cũ khai báo charset latin1. Dữ liệu trong DB hoàn toàn đúng, chỉ riêng đường ống xuất PDF là vỡ.

Bài học rút ra: encoding phải đúng ở toàn bộ đường ống, không chỉ ở DB. Là QA, đừng chỉ kiểm tra "dữ liệu lưu có đúng không", mà phải kiểm tra từng điểm dữ liệu đi qua: form nhập → API → DB → export PDF → email. Bug này lọt lưới vì tester chỉ verify màn hình web (đọc trực tiếp từ DB, đúng chuẩn) mà không mở file PDF xuất ra. Một test case đơn giản với input "Nguyễn Thị Lan" chạy xuyên suốt tới file PDF cuối cùng đã bắt được lỗi này.

Ví dụ 2 — App đặt món và ô tìm kiếm "trơ" với người dùng iPhone

Một app giao đồ ăn (giả định là FoodNow) nhận báo cáo lạ: một nhóm nhỏ người dùng tìm nhà hàng "Phở Thìn" không ra kết quả, dù nhà hàng chắc chắn tồn tại. Nhóm này gần như đều dùng iPhone. Nguyên nhân: khi merchant tạo nhà hàng qua web (Windows), tên được lưu dạng NFC; nhưng khi người dùng iPhone gõ "Phở Thìn" vào ô search, bàn phím iOS trả về chuỗi dạng NFD. Database so sánh byte-by-byte nên hai chuỗi "trông giống nhau" lại không khớp.

Đội QA đã tái hiện được bằng cách so sánh mã hex của hai chuỗi và thấy chữ "ở", "ì" khác byte. Fix: thêm bước normalize NFC cho cả dữ liệu lưu và query đầu vào.

Bài học rút ra: khi gặp bug "tìm không ra dù dữ liệu có tồn tại" liên quan đến một nhóm thiết bị cụ thể, hãy nghĩ ngay đến NFC/NFD. Và quan trọng: test dữ liệu tiếng Việt phải thực hiện trên nhiều nền tảng nhập liệu (iOS, Android, gõ trực tiếp, copy-paste từ Word), vì mỗi nguồn có thể sinh ra dạng chuẩn hóa khác nhau.

Ví dụ 3 — Import danh sách khách hàng từ Excel

Một công ty bảo hiểm nhập danh sách 5.000 khách hàng từ file Excel do nhân viên chi nhánh gửi lên. File được lưu từ Excel bản Windows tiếng Việt, mặc định định dạng ANSI (Windows-1258). Hệ thống backend giả định file là UTF-8 nên đọc vào, toàn bộ tên có dấu vỡ thành ký tự lạ, và 5.000 bản ghi lỗi được ghi thẳng vào production.

Bài học rút ra: với chức năng import, encoding của file nguồn là một biến số phải test rõ ràng. QA cần chuẩn bị bộ file mẫu ở nhiều encoding khác nhau (UTF-8, UTF-8 có BOM, ANSI/Windows-1258) và xác nhận hệ thống hoặc tự nhận diện đúng, hoặc báo lỗi rõ ràng — tuyệt đối không được "âm thầm nhập dữ liệu vỡ". Đây cũng là lời nhắc rằng nhiều lỗi encoding không nằm ở code, mà ở khâu con người tạo dữ liệu.

Hướng dẫn từng bước

Đây là quy trình bạn có thể áp dụng để kiểm thử data tiếng Việt cho bất kỳ tính năng nào.

Bước 1 — Chuẩn bị bộ dữ liệu test tiếng Việt chuẩn. Đừng dùng "abc". Hãy chuẩn bị sẵn một bộ chuỗi "vũ khí" phủ hết các trường hợp khó:

  • Đủ dấu thanh và dấu mũ chồng nhau: "Nguyễn Thị Hường", "Đặng Vũ Quỳnh".
  • Chữ "đ/Đ": "Đà Nẵng", "đường".
  • Chữ dễ vỡ: "ễ", "ộ", "ữ", "ằ", "ợ".
  • Chuỗi có cả emoji để test utf8mb4: "Yêu Việt Nam ❤️".
Bước 2 — Test đường đi của dữ liệu (round-trip). Nhập một chuỗi tiếng Việt ở đầu vào, rồi truy vết nó qua từng chặng: hiển thị lại trên web, lưu trong DB (query trực tiếp để xem), trả về qua API (đọc raw response), và mọi output phái sinh (PDF, email, SMS, file export). Chuỗi phải giống hệt ở mọi điểm.

Bước 3 — Kiểm tra cấu hình encoding. Nếu có quyền truy cập, xác nhận: DB dùng utf8mb4 (không phải utf8/latin1), HTTP response có header charset=UTF-8, HTML có thẻ <meta charset="utf-8">.

Bước 4 — Test NFC/NFD. Nhập cùng một chữ từ nhiều nguồn: gõ trên iPhone, gõ trên Android, copy-paste từ Word. So sánh kết quả lưu trong DB. Nếu cùng một chữ mà lưu ra byte khác nhau, hệ thống chưa normalize.

Bước 5 — Test search đầy đủ ma trận. Với mỗi từ khóa tiếng Việt, thử: có dấu ("cà phê"), không dấu ("ca phe"), viết hoa ("CÀ PHÊ"), chữ "đ" thay bằng "d" ("da nang" tìm "Đà Nẵng"). Ghi lại trường hợp nào ra kết quả, trường hợp nào không, đối chiếu với yêu cầu sản phẩm.

Bước 6 — Test sắp xếp (sorting). Danh sách tên tiếng Việt phải sắp đúng thứ tự bảng chữ cái Việt (a, ă, â, b... và các dấu thanh). Nhiều hệ thống sắp theo mã Unicode thô nên "Ăn" đứng sau "Bình", sai với trực giác người Việt.

Lỗi thường gặp & mẹo

Lỗi: chỉ test với "abc", "test123". Đây là nguyên nhân gốc khiến bug encoding lọt lưới. Mẹo: đặt bộ dữ liệu tiếng Việt "độc" thành mặc định trong mọi test case có trường nhập tên, địa chỉ, mô tả.

Lỗi: verify dữ liệu chỉ qua giao diện web. Web có thể tự "che" lỗi vì cùng một encoding từ đầu tới cuối. Mẹo: luôn kiểm tra thêm ở tầng DB (query trực tiếp) và các output phụ (PDF, email). Bug thường trốn ở những nhánh ít được nhìn tới.

Lỗi: bỏ qua trường hợp copy-paste. Người dùng thật thường copy tên từ Zalo, Word, Facebook — những nguồn có thể mang theo ký tự ẩn (zero-width space, non-breaking space) hoặc dạng NFD. Mẹo: đưa "paste từ nguồn ngoài" vào kịch bản test, không chỉ gõ tay.

Lỗi: nhầm lẫn giữa lỗi hiển thị và lỗi dữ liệu. Đôi khi dữ liệu trong DB đúng nhưng font hoặc trình duyệt hiển thị sai, và ngược lại. Mẹo: khi nghi ngờ, dùng công cụ xem mã hex/Unicode của chuỗi (nhiều website online cho phép dán chuỗi và xem từng code point) để biết chắc lỗi nằm ở tầng dữ liệu hay tầng hiển thị.

Mẹo về công cụ: Học cách đọc mã hex của một chuỗi UTF-8. Ví dụ, "Huế" ở dạng NFC và NFD sẽ có số byte khác nhau — nhìn hex là biết ngay. Đây là kỹ năng phân biệt tester "đoán mò" với tester "biết mình đang nói gì".

Mẹo về báo cáo bug: Khi report bug encoding, luôn đính kèm chuỗi input chính xác (tốt nhất là kèm mã hex), thiết bị/nền tảng nhập liệu, và ảnh chụp cả hai đầu (input và output vỡ). Bug encoding rất khó tái hiện nếu thiếu thông tin nguồn nhập, vì dev có thể gõ lại bằng bàn phím khác và không dựng lại được lỗi.

Bài tập thực hành

  • Bộ dữ liệu độc: Tự soạn một danh sách 15 chuỗi tiếng Việt phủ đủ: dấu thanh chồng dấu mũ, chữ đ/Đ, emoji, và một chuỗi copy-paste từ Word. Lưu lại để dùng cho mọi dự án sau này.
  • Săn NFC/NFD: Lấy một chữ như "Huế", tạo bản NFC và bản NFD (nhiều công cụ online hỗ trợ), so sánh số byte và mã hex của hai bản. Viết lại khác biệt bạn quan sát được bằng lời của mình.
  • Ma trận search: Chọn một website TMĐT Việt Nam bất kỳ (ví dụ một trang bán hàng bạn hay dùng). Thử tìm cùng một sản phẩm bằng 4 cách: có dấu, không dấu, viết hoa, thay "đ" bằng "d". Lập bảng ghi lại cách nào ra kết quả, cách nào không, và kết luận search của trang đó "hiểu tiếng Việt" tới đâu.
  • Truy vết round-trip: Với một form đăng ký bất kỳ (có thể là chính website bạn đang test), nhập tên "Đặng Thị Hường ❤️", rồi kiểm tra chuỗi này hiển thị lại ở: trang profile, email xác nhận (nếu có), và bất kỳ nơi nào khác. Ghi lại điểm nào giữ nguyên, điểm nào vỡ.

Tóm tắt

Test data tiếng Việt xoay quanh hai trục cốt lõi. Thứ nhất là encoding: chữ có dấu phải đi qua toàn bộ đường ống dữ liệu (form → API → DB → export → email) mà không biến chất, và cạm bẫy phổ biến nhất là DB đặt sai charset (latin1 hoặc utf8mb3 thay vì utf8mb4) khiến "Nguyễn" thành "Nguyá»…n". Thứ hai là hiện tượng NFC vs NFD: cùng một chữ "ế" có thể được biểu diễn bằng số byte khác nhau tùy nguồn nhập (iOS thường NFD, Windows/Android thường NFC), gây ra những bug "so sánh không khớp" và "tìm không ra" cực khó chịu — giải pháp là normalize toàn bộ về NFC. Về search, một hệ thống phục vụ người Việt phải xử lý được tìm kiếm bỏ dấu, bỏ hoa/thường và thay "đ" bằng "d", vì đó là thói quen gõ tay thực tế của người dùng.

Điều cần nhớ nhất: đừng bao giờ test tính năng phục vụ người Việt bằng "abc" và "test123". Lợi thế lớn nhất của một QA người Việt là bạn biết chính xác chữ nào khó, người dùng gõ kiểu gì, và output thế nào là "vỡ" — hãy biến kiến thức bản ngữ đó thành một bộ dữ liệu test có hệ thống và một quy trình truy vết round-trip xuyên suốt. Đó là thứ tách một tester bình thường khỏi một tester thực sự hiểu sản phẩm mình đang bảo vệ.