使用Mockito/JUnit测试含泛型的final void方法触发异常问题
我尝试用Mockito和JUnit测试一个包含泛型参数的void方法,期望通过doNothing()模拟该方法,但运行测试时触发异常;而无泛型参数的error方法却能正常运行。两者的差异在于success方法被定义为final,error方法则不是。
抛出的异常信息
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))Also, this error might show up because you use argument matchers with
methods that cannot be mocked. Following methods cannot be
stubbed/verified: final/private/equals()/hashCode(). Mocking methods
declared on non-public parent classes is not supported.
相关代码
待测试类 GreenWork.java
public class GreenWork { @Autowired Service service; public void getDataResponses() { ResponseEntity<DataDTO> responseEntity = restTemplate.exchange( settings.getUrk(), HttpMethod.GET, null, DataDTO.class); if (responseEntity.getStatusCode().is4xxClientError()) { ErrorData errorData = new ErrorData(); errorData.setMessage("error loading"); service.error("corId", errorData, "otherId"); } else { service.success("corId", responseEntity.getBody(), "otherId"); } } }
服务类 Service.java
public final void success( final String corId, final T t, // Generic class final String otherud) throws Exception { ...... restTemplate.exchange( "url"), entity, Void.class); } public void error( final String corId, final ErrorData errorData, final String otherud) throws Exception { ...... restTemplate.exchange( "url"), entity, Void.class); }
JUnit测试代码
@InjectMocks private GreenWork greenWork; @Test public void shoulSuccessCallbak() { doNothing().when(service).success(anyString(), any(), anyString()); // 触发Exception 1异常 doNothing().when(service).success(anyString(), any(GrayDTO.class), anyString()); // 同样触发异常 doNothing().when(service).error("corId", any(ErrorData.class), "xId"); // 无异常,正常运行 greenWork.getDataResponses(); }
解决方案
问题核心不是泛型,而是Mockito默认不支持mock final方法,异常提示里已经明确说明:final方法无法被stub/verify。success方法是final修饰的,所以不管用不用泛型参数匹配,都会触发异常;而error方法不是final,因此能正常mock。
解决办法有两种:
- 方案1(优先推荐,若允许修改源码):去掉
Service类中success方法的final修饰符,修改后Mockito就能正常模拟该方法。 - 方案2(无法修改源码时使用):启用Mockito的Final Mock功能:
- 给项目添加
mockito-inline依赖(版本需与Mockito核心版本一致); - 在
src/test/resources/mockito-extensions目录下创建org.mockito.plugins.MockMaker文件,文件内容仅需一行:mock-maker-inline,即可全局启用final方法的mock能力。
- 给项目添加
内容的提问来源于stack exchange,提问作者user1814879

