Bài 14: Kiểm thử (Testing) AOP trong Spring Boot

Mục tiêu bài học
Hiểu các thách thức khi kiểm thử AOP: Aspect “ẩn” trong proxy, phải test cả method gốc và advice.
Biết cách kiểm thử logic gốc vs. logic aspect: Phân biệt giữa unit test và integration test.
Thực hiện unit test, integration test cho AOP: Dùng
Mockito
,SpringBootTest
để kiểm tra aspect có hoạt động đúng.Debug proxy, verify advice chạy đúng: Cách kiểm tra xem Aspect có thực sự kích hoạt.
Nội dung chính
1. Khó khăn khi test AOP
Aspect không dễ thấy: Không thể gọi trực tiếp method của aspect mà phải test gián tiếp qua class bị AOP quản lý.
Pointcut có thể không match: Nếu pointcut không khớp, aspect sẽ không chạy => cần kiểm tra kỹ.
Proxy-based AOP ảnh hưởng đến test:
Spring AOP tạo proxy cho bean => khó test nếu dùng
@Mock
.Nếu method gọi chính nó (
self-invocation
), AOP sẽ không chạy.
Ví dụ lỗi thường gặp khi test AOP:
MethodNotInterceptedException: Method was not intercepted by Aspect
\=> Nguyên nhân: Test không chạy trên Spring context, dẫn đến aspect không hoạt động.
2. Unit Test vs. Integration Test trong AOP
Loại test | Mục đích | Ưu điểm | Nhược điểm |
Unit Test | Kiểm tra logic bên trong aspect | Nhanh, không cần khởi động Spring | Không kiểm tra toàn bộ flow AOP |
Integration Test | Kiểm tra AOP chạy đúng trên ứng dụng thực tế | Kiểm tra toàn bộ hệ thống | Chạy chậm hơn |
3. Unit Test Aspect (Dùng Mockito)
- Mock
JoinPoint
để kiểm tra logic aspect hoạt động đúng.
3.1. Viết Aspect cần test
Aspect đo thời gian thực thi method:
@Aspect
@Component
public class PerformanceAspect {
private static final Logger logger = LoggerFactory.getLogger(PerformanceAspect.class);
@Around("@annotation(com.example.PerformanceMonitor)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Execution time of {}: {} ms", joinPoint.getSignature(), elapsedTime);
return result;
}
}
3.2. Viết Unit Test với Mockito
- Mock
ProceedingJoinPoint
để giả lập gọi method thực tế.
@ExtendWith(MockitoExtension.class)
class PerformanceAspectTest {
@InjectMocks
private PerformanceAspect performanceAspect;
@Mock
private ProceedingJoinPoint joinPoint;
@Test
void testMeasureExecutionTime() throws Throwable {
when(joinPoint.proceed()).thenReturn("Success");
when(joinPoint.getSignature()).thenReturn(() -> "mockMethod()");
Object result = performanceAspect.measureExecutionTime(joinPoint);
assertEquals("Success", result);
verify(joinPoint, times(1)).proceed();
}
}
📌 Giải thích:
Mock
ProceedingJoinPoint
để giả lập việc gọi method thực tế.Gọi aspect
measureExecutionTime
và kiểm tra:joinPoint.proceed()
có được gọi đúng số lần không?Kết quả trả về có đúng không?
4. Integration Test AOP (SpringBootTest)
- Chạy Spring context để kiểm tra aspect thực sự hoạt động.
4.1. Viết Annotation @PerformanceMonitor
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PerformanceMonitor {
}
4.2. Viết Service để kiểm tra Aspect
@Service
public class DemoService {
@PerformanceMonitor
public String process() {
return "Processing...";
}
}
4.3. Viết Integration Test
@SpringBootTest
@AutoConfigureMockMvc
class PerformanceAspectIT {
@Autowired
private DemoService demoService;
@Test
void testPerformanceAspect() {
String result = demoService.process();
assertEquals("Processing...", result);
// Kiểm tra log có đúng không (có thể dùng LogCaptor)
}
}
📌 Giải thích:
Khởi chạy Spring context (
@SpringBootTest
) để đảm bảo AOP thực sự chạy.Gọi method
demoService.process()
và kiểm tra kết quả có đúng không.Có thể kiểm tra log bằng
LogCaptor
hoặc AssertJ.
5. Debugging Aspect
- Bật log debug để kiểm tra proxy và pointcut
logging.level.org.springframework.aop=DEBUG
- Kiểm tra proxy đang dùng (
CGLIB
hayJDK Dynamic Proxy
)
System.out.println(AopProxyUtils.ultimateTargetClass(demoService));
- Kiểm tra method có bị AOP bắt hay không
System.out.println(AopUtils.isAopProxy(demoService)); // True nếu có proxy
6. Best Practices for Testing AOP
✅ Viết test cho cả unit và integration
Unit Test: Kiểm tra logic bên trong aspect.
Integration Test: Kiểm tra xem aspect có thực sự chạy trong Spring context không.
✅ Kiểm tra log hoặc hành vi bị ảnh hưởng bởi AOP
Dùng LogCaptor để kiểm tra log nếu cần.
Ghi assertion trên giá trị trả về nếu aspect thay đổi kết quả method.
✅ Tránh mock toàn bộ Spring bean trong Integration Test
- Dùng
@SpringBootTest
thay vì mock bean bị AOP quản lý.
✅ Đảm bảo Pointcut chính xác
- Dùng debug hoặc log để kiểm tra aspect có thực sự chạy không.
Tóm tắt
Unit test aspect bằng Mockito: Giả lập
JoinPoint
, kiểm tra logic bên trong aspect.Integration test để kiểm tra aspect chạy thực tế: Dùng
@SpringBootTest
, gọi method có AOP.Debugging Aspect: Bật log debug, kiểm tra proxy, validate pointcut.
Best practices: Viết test cho cả unit và integration, đảm bảo AOP chạy đúng.
✅ Sang Bài 15: Tổng kết, Best Practices, Pitfalls của Spring AOP.
Câu hỏi thảo luận
Khi nào nên mock aspect thay vì chạy thực?
- Nếu aspect không ảnh hưởng đến logic chính nhưng có thể làm chậm test.
Bài học kinh nghiệm test AOP trong production?
- Debug pointcut kỹ, kiểm tra overhead do AOP gây ra.
Subscribe to my newsletter
Read articles from hoangkim directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
