JUnit Platform, Jupiter và execution lifecycle
JUnit hiện đại là kiến trúc launcher–engine, không chỉ là một bộ annotation để viết test. Hiểu các lớp này giúp phân biệt lỗi compile, discovery, execution và build integration.
1. Kiến trúc Platform, Engine và Jupiter
JUnit Platform là nền tảng launch test engines, cung cấp discovery, Launcher API và integration với IDE/build tool. TestEngine chịu trách nhiệm tạo test plan, cây descriptor và thực thi node. JUnit Jupiter cung cấp programming model và extension model hiện đại để viết/chạy test.
Vì vậy, compile thành công không bảo đảm test đã được discover. Một project có thể có Jupiter API để biên dịch nhưng thiếu Jupiter engine, cấu hình plugin sai hoặc filter khiến CI không chạy test nào.
2. Discovery → execution → report
- IDE hoặc build plugin tạo request với selector, filter và configuration parameters.
- Platform hỏi từng
TestEngineđể discover test plan. - Engine tạo container/test nodes, chạy lifecycle callback và phát execution events.
- Launcher/build tool thu report, xử lý failure và quyết định exit status.
Pipeline có thể “xanh giả” nếu test bị filter, disable hoặc không được discover nhưng build không có fail-fast check cho số lượng test đã chạy. CI nên lưu report và có policy phát hiện test count bất thường.
3. Test instance và fixture lifecycle
Mặc định JUnit tạo một test instance mới cho mỗi test method (PER_METHOD). Đây là lựa chọn an toàn hơn vì giảm shared mutable state và order dependency. PER_CLASS dùng lại instance giữa các method, cho phép non-static lifecycle methods nhưng làm state dễ leak giữa test và tăng rủi ro khi bật parallel execution.
Với fixture dùng chung, phải xác định ownership, cleanup và thread-safety. Database record, system property, file tạm, port và external resource không được tồn tại ngầm sau test.
@ParameterizedTest
@CsvSource({"0,false", "1,true", "10,true"})
void validatesQuantity(int quantity, boolean expected) {
assertEquals(expected, OrderRules.isValidQuantity(quantity));
}
4. Parameters, extensions và parallelism
Parameterized tests
@ParameterizedTest giúp diễn đạt nhiều input/output case mà không nhân bản cùng một test method. Input nên đại diện cho boundary và failure case có ý nghĩa; đừng biến một test thành bảng dữ liệu khổng lồ không còn dễ đọc.
Extensions
Extension có thể tham gia condition evaluation, parameter resolution, lifecycle callback và exception handling. Chỉ tạo extension cho concern tái sử dụng xuyên suite; nếu logic chỉ phục vụ một test, fixture bình thường thường dễ đọc và dễ debug hơn.
Parallel execution
Parallel test cần isolation hoặc resource lock cho system properties, files, ports, database state và mock/thread safety. Chuyển sang parallel không tự động làm pipeline nhanh hơn nếu fixture contention và container startup trở thành bottleneck.
5. Failure modes và cách chẩn đoán
- Local/CI discovery khác nhau: pin plugin/engine version, kiểm tra dependency tree và test report thực tế.
- Filter hoặc
@Disablednhầm: review tag, selector, naming convention và cảnh báo test count bằng không. - Timeout quá rộng: deadlock biến thành pipeline treo; đặt timeout theo expected bound và thu thread dump khi timeout.
- Extension che exception gốc: giới hạn exception handling và log cause chain đầy đủ.
- Resource leak: cleanup trong lifecycle phù hợp và assert resource được đóng sau test.
- Parallel race: cô lập state, dùng lock có chủ đích và không dựa vào thứ tự method.
6. Câu hỏi phỏng vấn đào sâu
- Platform khác Jupiter như thế nào, và TestEngine nằm ở đâu?
- Vì sao test chạy được trong IDE nhưng Maven/Gradle không discover?
PER_CLASStạo rủi ro order dependency nào?- Bật parallel test có thể làm lộ race trong fixture và Testcontainers setup ra sao?
- Làm cách nào phân biệt test không được discover với test đã chạy nhưng bị skip?
7. Checklist tự đánh giá
- Tôi phân biệt được Platform, TestEngine và Jupiter.
- Tôi mô tả được discovery, execution và report flow.
- Tôi biết khi nào dùng
PER_METHODvà rủi ro củaPER_CLASS. - Tôi thiết kế extension cho reusable concern, không che giấu logic test.
- Tôi audit shared state trước khi bật parallel execution.
- Tôi có thể chẩn đoán trường hợp IDE chạy nhưng CI không discover test.