新增Aspect单元测试引发Mockito相关测试失败问题排查求助
问题描述
为自定义Aspect新增单元测试后,批量运行所有测试时多数用例出现Mockito错误(未完成存根、空指针异常、Mock释放失败等),单独运行该测试正常,移除后所有测试恢复正常。已尝试统一spring-boot-starter-test与mockito-inline版本,未解决问题。
注解代码
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ValidCheck { boolean enabled() default true; }
切面代码
@Aspect @Component @Slf4j public class ValidCheckService { @Value("${field.to.validate}") private String field; @Around(value = "@annotation(com.test.logging.annotations.ValidCheck)") public ResponseEntity<?> validateFields(ProceedingJoinPoint pjp) throws Throwable { try { Object[] args = pjp.getArgs(); var validCheck = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class); if (validCheck.enabled()) { if (args != null && args.length > 0) { var someClass = Arrays.stream(args) .filter(arg -> arg instanceof SomeClass) .findAny() .map(arg -> (SomeClass) arg) .orElseThrow(() -> new IllegalArgumentException("No SomeClass present")); var fieldToCheck = someClass.getField(); if (!field.equalsIgnoreCase(fieldToCheck)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } } } else { log.info("Check disabled"); } } catch (Exception e) { log.error("Exception in validating fields", e); } return (ResponseEntity<?>) pjp.proceed(); } }
测试类代码
class ValidCheckServiceTest { private ProceedingJoinPoint pjp; private SomeClass some; private ValidCheck validCheck; private ValidCheckService validCheckService; @BeforeEach public void init() { pjp = mock(ProceedingJoinPoint.class); some = mock(SomeClass.class); validCheck = mock(ValidCheck.class); MethodSignature methodSignature = mock(MethodSignature.class); Method method = mock(Method.class); validCheckService = new ValidCheckService(); setField(validCheckService, "field", SOME_FIELD); when(pjp.getSignature()).thenReturn(methodSignature); when(methodSignature.getMethod()).thenReturn(method); when(method.getAnnotation(ValidCheck.class)).thenReturn(validCheck); } @Test void test1() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{some, new Object()}); when(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class)).thenReturn(validCheck); when(validCheck.enabled()).thenReturn(true); when(some.getClientId()).thenReturn("value"); var responseEntity = validCheckService.validateFields(pjp); assertEquals(HttpStatus.FORBIDDEN, responseEntity.getStatusCode()); } @Test void test2() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{some, new Object()}); when(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class)).thenReturn(validCheck); when(validCheck.enabled()).thenReturn(true); when(some.getClientId()).thenReturn(SOME_FIELD); when(pjp.proceed()).thenReturn(ResponseEntity.ok().build()); var responseEntity = validCheckService.validateFields(pjp); assertNotEquals(HttpStatus.FORBIDDEN, responseEntity.getStatusCode()); } @Test void test3() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{some, new Object()}); when(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class)).thenReturn(validCheck); when(validCheck.enabled()).thenReturn(false); when(pjp.proceed()).thenReturn(ResponseEntity.ok().build()); var responseEntity = validCheckService.validateFields(pjp); assertNotEquals(HttpStatus.FORBIDDEN, responseEntity.getStatusCode()); } @ParameterizedTest @ValueSource(booleans = {true, false}) void test4(boolean enabled) throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{new Object()}); when(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class)).thenReturn(validCheck); when(validCheck.enabled()).thenReturn(enabled); when(pjp.proceed()).thenReturn(ResponseEntity.ok().build()); var responseEntity = validCheckService.validateFields(pjp); assertNotEquals(HttpStatus.FORBIDDEN, responseEntity.getStatusCode()); } private static final String SOME_FIELD = "field"; }
当前依赖版本
+- org.mockito:mockito-inline:jar:4.5.1:test +- org.mockito:mockito-junit-jupiter:jar:4.5.1:test +- org.mockito:mockito-core:jar:3.5.13:test
问题根源分析
- Mockito版本冲突:当前依赖中
mockito-core为3.5.13,而mockito-inline和mockito-junit-jupiter是4.5.1,跨大版本的混合依赖会导致内部API不兼容,引发Mock对象初始化、存根逻辑异常,批量测试时类加载器复用这些不一致的类,问题暴露更明显。 - Aspect测试的Mock污染:测试中直接实例化
ValidCheckService并通过反射设置字段,但该类是Spring管理的@Component,批量测试时Spring上下文可能复用这个实例,导致Mock对象残留到其他测试中,干扰正常Bean生命周期。 - 存根重复定义:测试方法中重复定义了
when(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ValidCheck.class)).thenReturn(validCheck),而@BeforeEach已经完成相同存根,重复存根会导致Mockito内部状态混乱,批量运行时累积错误。 - 未清理Mock状态:测试类未使用
@ExtendWith(MockitoExtension.class),Mockito无法自动在测试后重置Mock状态,批量运行时Mock的存根会在测试间泄漏。
内容的提问来源于stack exchange,提问作者Hindol Dey
相关产品推荐
相关产品推荐

