如何测试带@Async注解方法中私有方法的异常场景?
测试@Async方法中私有方法的异常场景
针对你的场景,有几种可行的方案来测试私有方法anotherMethod的异常抛出逻辑:
1. 通过反射直接调用私有方法
利用Java反射机制绕过访问权限,直接调用私有方法,配合Mockito模拟restTemplate的返回,验证异常是否正确抛出。
示例代码:
@ExtendWith(MockitoExtension.class) public class YourServiceTest { @InjectMocks private YourService service; @Mock private RestTemplate restTemplate; @Test void testAnotherMethodThrowsWhenResponseIsNull() throws Exception { // 模拟restTemplate返回null Mockito.when(restTemplate.postForObject( Mockito.anyString(), Mockito.any(Request.class), Mockito.eq(Response.class) )).thenReturn(null); // 通过反射获取私有方法 Method method = YourService.class.getDeclaredMethod("anotherMethod", Request.class); method.setAccessible(true); // 开启访问权限 // 执行方法并验证异常 Request testReq = new Request(); assertThrows(RemoteServiceException.class, () -> { method.invoke(service, testReq); }); } @Test void testAnotherMethodThrowsWhenResponseNotOk() throws Exception { // 模拟返回非OK的响应 Response mockResponse = Mockito.mock(Response.class); Mockito.when(mockResponse.isOk()).thenReturn(false); Mockito.when(restTemplate.postForObject( Mockito.anyString(), Mockito.any(Request.class), Mockito.eq(Response.class) )).thenReturn(mockResponse); Method method = YourService.class.getDeclaredMethod("anotherMethod", Request.class); method.setAccessible(true); Request testReq = new Request(); RemoteServiceException exception = assertThrows(RemoteServiceException.class, () -> { method.invoke(service, testReq); }); // 验证异常信息和错误码 assertEquals(ErrorCodeConstant.Exception, exception.getErrorCode()); assertEquals("some message", exception.getMessage()); } }
2. 重构代码(推荐方案)
反射测试私有方法会让测试和实现细节耦合,长期维护成本高。更合理的方式是重构代码,将私有方法的逻辑抽离:
方案A:将逻辑移到独立组件
把anotherMethod的逻辑提取到一个新的、可测试的类(比如RemoteServiceCaller),原服务类依赖这个组件。这样可以直接测试新类的方法,无需关心原类的@Async修饰。
重构后代码示例:
// 新的独立组件 @Component public class RemoteServiceCaller { private final RestTemplate restTemplate; public RemoteServiceCaller(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public Response callRemoteService(Request req) { String uri = getUrl(); Response response = restTemplate.postForObject(uri, req, Response.class); if (response == null) { throw new RemoteServiceException(); } if (!response.isOk()) { throw new RemoteServiceException(ErrorCodeConstant.Exception, "some message"); } return response; } private String getUrl() { // 原逻辑保留 return "http://example.com/api"; } } // 原服务类 @Service public class YourService { private final RemoteServiceCaller caller; public YourService(RemoteServiceCaller caller) { this.caller = caller; } @Async public CompletableFuture<Response> method(Request req) { Response response = caller.callRemoteService(req); return CompletableFuture.completedFuture(response); } }
此时直接测试RemoteServiceCaller的callRemoteService方法即可,完全不需要处理@Async的影响。
方案B:将私有方法改为protected
如果不想新增类,可以把anotherMethod的访问修饰符改为protected,然后在测试类中创建原服务类的子类,直接调用该方法测试。
3. 测试@Async方法的CompletableFuture结果
虽然@Async方法是异步执行,但它返回的CompletableFuture会封装执行过程中的异常。可以通过调用CompletableFuture.get()方法获取结果,捕获ExecutionException,然后验证底层异常类型。
示例代码:
@ExtendWith(MockitoExtension.class) public class YourServiceTest { @InjectMocks private YourService service; @Mock private RestTemplate restTemplate; @Test void testMethodThrowsWhenResponseIsNull() throws InterruptedException, ExecutionException { Mockito.when(restTemplate.postForObject( Mockito.anyString(), Mockito.any(Request.class), Mockito.eq(Response.class) )).thenReturn(null); CompletableFuture<Response> future = service.method(new Request()); ExecutionException exception = assertThrows(ExecutionException.class, future::get); // 验证底层异常是RemoteServiceException assertTrue(exception.getCause() instanceof RemoteServiceException); } }
这种方式不需要直接操作私有方法,而是通过测试公开方法的异步结果来间接验证私有方法的逻辑。
内容的提问来源于stack exchange,提问作者Khaski
相关产品推荐
相关产品推荐

