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(...):

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:

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_testscho Hoot unit test;web.assets_testscho 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/tinhtrỏ tớiquan_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ề
- Odoo 19 dùng Hoot thay QUnit cho test frontend. Cú pháp hiện đại:
describegom nhóm,testlà một ca,expect(x).toBe(y)/toEqual/toThrowkhẳng định — quen thuộc với ai từng dùng Jest. - Đặt test trong
static/tests/*.test.js, khai vào bundleweb.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/.... - 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.