如何在调用pjp.proceed时抛出FooStudioException并模拟SpamException构造方法
问题场景
现有异常类
FooStudioException
public class FooStudioException extends SpamException { public FooStudioException(String message, String localizedMessage, Throwable cause) { super(SpamErrorCodes.INTERNAL_ERROR, message, localizedMessage, cause); } public FooStudioException(String message, String localizedMessage) { super(SpamErrorCodes.INTERNAL_ERROR, message, localizedMessage); } }
SpamException
public class SpamException extends Exception { // 其他代码省略 public SpamException(SpamErrorCodes codes, String message, String localizedMessage, Throwable cause) { super(message, cause); // 其他逻辑省略 } }
切面实现
FooInstrumentAspect
public class FooInstrumentAspect extends InstrumentAspect { @Pointcut("@annotation(Foo.aop.annotation.FooInstrument)") public void FooInstrumentPointcut() {} @Around("FooInstrumentPointcut()") public Object aroundFooInstrumented(ProceedingJoinPoint pjp) throws Throwable { try { return aroundInstrumented(pjp); } catch (FooStudioException ex) { throw ex.getCause(); } } }
InstrumentAspect
@Aspect public class InstrumentAspect { // 其他代码省略 @Around("instrumentPointcut()") public Object aroundInstrumented(ProceedingJoinPoint pjp) throws Throwable { Object result = null; Throwable throwable = null; try { result = pjp.proceed(); } catch (Throwable t) { throwable = t; } if (throwable != null) { throw throwable; } return result; } }
需求:测试时让pjp.proceed()抛出FooStudioException,同时模拟SpamException的构造方法,避免执行其真实逻辑,使用PowerMock实现。
实现步骤
1. 配置测试类注解
测试类必须添加PowerMock核心注解,指定需要处理的目标类:
@RunWith(PowerMockRunner.class) @PrepareForTest({SpamException.class, FooStudioException.class}) public class FooInstrumentAspectTest { // 测试代码 }
@RunWith(PowerMockRunner.class):指定测试用PowerMock运行器执行@PrepareForTest:标记需要被PowerMock拦截构造方法的类,此处包含SpamException和FooStudioException
2. 模拟SpamException构造方法
用PowerMock的whenNew方法拦截SpamException的构造调用,返回模拟对象以跳过真实构造逻辑:
// 创建SpamException模拟实例 SpamException mockSpamEx = Mockito.mock(SpamException.class); // 模拟带Throwable参数的构造方法 PowerMockito.whenNew(SpamException.class) .withArguments(SpamErrorCodes.INTERNAL_ERROR, anyString(), anyString(), any(Throwable.class)) .thenReturn(mockSpamEx); // 可选:模拟无Throwable参数的构造方法(根据测试场景需求添加) PowerMockito.whenNew(SpamException.class) .withArguments(SpamErrorCodes.INTERNAL_ERROR, anyString(), anyString()) .thenReturn(mockSpamEx);
3. 让pjp.proceed()抛出FooStudioException
构造测试用的FooStudioException实例,然后设置ProceedingJoinPoint的proceed方法抛出该异常:
// 创建测试用的异常根因 Throwable testCause = new RuntimeException("测试触发的异常"); // 构造FooStudioException实例 FooStudioException fooEx = new FooStudioException("错误提示", "本地化错误信息", testCause); // 模拟ProceedingJoinPoint对象 ProceedingJoinPoint pjp = Mockito.mock(ProceedingJoinPoint.class); // 指定调用proceed时抛出FooStudioException Mockito.when(pjp.proceed()).thenThrow(fooEx);
4. 执行测试并验证逻辑
调用切面方法,验证是否按预期抛出FooStudioException的根因,同时确认SpamException的构造方法被正确调用:
FooInstrumentAspect aspect = new FooInstrumentAspect(); // 执行切面逻辑,预期会抛出testCause try { aspect.aroundFooInstrumented(pjp); Assert.fail("未抛出预期的RuntimeException"); } catch (RuntimeException e) { Assert.assertEquals("测试触发的异常", e.getMessage()); } // 验证SpamException的构造方法是否被调用 PowerMockito.verifyNew(SpamException.class) .withArguments(SpamErrorCodes.INTERNAL_ERROR, "错误提示", "本地化错误信息", testCause);
注意事项
- 若使用JUnit 5,需替换为
@ExtendWith(PowerMockExtension.class)替代@RunWith(PowerMockRunner.class) - 确保
@PrepareForTest包含所有需要模拟构造的类,否则PowerMock无法拦截构造调用 - 模拟构造方法可隔离
SpamException内部的复杂逻辑,保证测试仅关注切面的异常处理逻辑
内容的提问来源于stack exchange,提问作者Abhijit
相关产品推荐
相关产品推荐

