Suốt loạt này ta viết rất nhiều JavaScript cho web client — component, widget, service. Nhưng làm sao biết chúng đúng mà không phải mở trình duyệt bấm thử từng lần? Câu trả lời là test frontend. Và ở Odoo 19 có một thay đổi lớn: bộ khung test cũ QUnit đã lùi lại, thay bằng Hoot — framework test riêng của Odoo, cú pháp hiện đại kiểu Jest/Vitest.

Hoot: describe / test / expect

Ai từng viết test JS hiện đại sẽ thấy quen ngay. Test gom trong describe, mỗi ca là một test, khẳng định bằng expect(...).toBe(...):

Ảnh chụp hai đoạn mã nền tối. Đoạn trên file tinh js export hàm thuần tinhTongDiem nhận dsDiem trả về reduce cộng dồn bỏ qua null, và hàm xepHang nhận diem trả về vang nếu từ 150, bac nếu lớn hơn 0, moi nếu còn lại. Đoạn dưới file static tests tinh test js import describe test expect từ @odoo/hoot và import tinhTongDiem xepHang từ @quan_ca_phe js tinh, describe quan_ca_phe tính điểm chứa test tinhTongDiem cộng đúng bỏ qua null với expect tinhTongDiem của mảng 150 85 0 150 toBe 385 và expect của mảng 10 null 5 toBe 15, và test xepHang theo mốc điểm với expect xepHang 150 toBe vang và expect xepHang 85 toBe bac, kèm chú thích khai vào bundle web assets_unit_tests chạy tại web tests

Hình 1: Test cho hai hàm thuần. import { describe, test, expect } from "@odoo/hoot" là cú pháp Hoot của Odoo 19. describe gom nhóm; test là một ca; expect(giá_trị).toBe(mong_đợi) khẳng định. Ngoài toBe còn có toEqual (so sâu), toThrow, toHaveText... File test đặt trong static/tests/, đuôi .test.js, và khai vào bundle web.assets_unit_tests (khác web.assets_tests dành cho tour).

Vì sao test hàm thuần trước

Để ý mình chọn hàm thuần (tinhTongDiem, xepHang) để test đầu tiên — không phải component. Đó là chủ đích: hàm thuần nhận đầu vào, trả đầu ra, không phụ thuộc DOM hay server, nên test cực dễ và cực nhanh. Đây là lý do ở bài sub-component và bài getter, việc tách logic tính toán ra khỏi phần vẽ không chỉ cho code sạch mà còn cho code dễ test. Component cần mount, cần fixture; hàm thuần thì chỉ gọi và so kết quả.

Chạy thật ở /web/tests

Odoo có sẵn một trình chạy test trong trình duyệt tại /web/tests (Hoot runner). Khai test vào bundle, mở trang đó, lọc theo tên module, và Hoot chạy ngay:

Ảnh chụp thật trình chạy test Hoot của Odoo 19, thanh trên cùng có chữ HOOT nút Run và ô lọc gõ quan_ca_phe, dòng trạng thái ghi 2 tests completed total time 00 00 00 với dấu tích xanh và số 2 bên phải. Khu kết quả liệt kê hai ca test đều chấm xanh lá đã qua, ca một tinhTongDiem cộng đúng bỏ qua null 8 mili giây, ca hai xepHang theo mốc điểm 4 mili giây; cột trái là cây các bộ test theo module

Hình 2: Trình chạy Hoot thật. Ô lọc gõ "quan_ca_phe" để chỉ chạy test của module này. Kết quả: "2 tests completed" với dấu tích xanh và ✓2 — cả hai ca qua (chấm xanh): tinhTongDiem... (8ms), xepHang... (4ms). Cột trái là toàn bộ cây test của mọi module đã cài (@website 635, @crm 32...), bạn lọc để chỉ chạy phần cần. Đây là kết quả thật, không dàn dựng.

Vài điều thực dụng

  • web.assets_unit_tests cho Hoot unit test; web.assets_tests cho tour. Khai nhầm bundle là test không xuất hiện trong runner — họ hàng với cái bẫy bundle ở bài assets.
  • Đường dẫn import theo alias module: @quan_ca_phe/js/tinh trỏ tới quan_ca_phe/static/src/js/tinh.js. Sai alias là test không tìm thấy hàm cần kiểm.
  • Lọc để chạy nhanh. Cây test đầy đủ của Odoo có hàng nghìn ca; lọc theo module/tên khi phát triển, chạy toàn bộ khi cần chắc chắn.
  • Test component cần mountOnFixture (từ @odoo/hoot-dom) và các helper mô phỏng — nặng hơn test hàm thuần. Bắt đầu từ hàm thuần, tiến tới component khi đã quen.

Ba ý mang về

  1. Odoo 19 dùng Hoot thay QUnit cho test frontend. Cú pháp hiện đại: describe gom nhóm, test là một ca, expect(x).toBe(y)/toEqual/toThrow khẳng định — quen thuộc với ai từng dùng Jest.
  2. Đặt test trong static/tests/*.test.js, khai vào bundle web.assets_unit_tests, rồi chạy trong trình duyệt tại /web/tests (lọc theo module để chạy nhanh). Import mã cần test theo alias @module/....
  3. Bắt đầu từ hàm thuần. Tách logic tính toán ra khỏi phần vẽ khiến code vừa sạch vừa dễ test — hàm thuần chỉ cần gọi và so kết quả, không cần mount hay server.

Hoot unit test kiểm một mảnh code. Nhưng còn cả một luồng người dùng — bấm nút này, điền ô kia, sang màn hình nọ — thì kiểm thế nào? Phần sau nói về tour: kịch bản mô phỏng người dùng thao tác qua nhiều bước, dùng để kiểm thử cả một quy trình từ đầu tới cuối.