Mockito单元测试如何Mock内部调用依赖类并自定义返回值
问题核心原因
你当前测试代码不生效的核心问题有两个:
- 你创建的class2 mock实例没有被被测方法实际调用:现有
methodToBeTested方法内部要么直接硬编码实例化class2、要么调用class2的静态方法,外部创建的mock对象根本传不进执行链路,自然不会按你预设的规则返回值。 - 如果你调用的是class2的静态方法,普通Mockito的
mock()方法无法拦截静态调用,需要用专门的静态mock能力。
标准实现方案(推荐,符合Spring Boot开发规范)
Spring Boot项目本身推荐构造器注入依赖,这种写法天然适配单元测试,不需要额外改测试逻辑,步骤如下:
- 改造被测类Class1,将所有依赖(包括class2以及其他你要mock的类)作为类成员,通过构造器注入,禁止在业务方法内部直接new依赖实例。
改造后的Class1示例:
// 类名遵循Java大驼峰命名规范 @Component public class Class1 { // 所有依赖作为final成员变量 private final Class2 class2; // 多个依赖直接加在构造器参数里即可,比如Class3、Class4 // private final Class3 class3; // 构造器注入,Spring运行时会自动注入对应实例,单测时可以手动传入mock对象 public Class1(Class2 class2) { this.class2 = class2; } public Class1ReturnType methodToBeTested(/* 你的业务入参 */) { // 省略其他业务逻辑 // 调用注入的class2实例的方法,不要写new Class2().method()或者Class2.method()这种硬编码 ReturnData rd = class2.targetMethod(/* 业务入参 */); // 省略后续基于rd的处理逻辑 return returnObj; } }
- 编写单元测试时,先给mock对象定义返回规则,再注入被测类执行方法。
纯Mockito单元测试(不启动Spring容器,执行速度最快)
import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class Class1Test { @Test void testMethodToBeTested_SuccessScenario() { // 第一步:mock所有依赖 Class2 mockClass2 = mock(Class2.class); // 其他依赖同理mock:Class3 mockClass3 = mock(Class3.class); // 第二步:按当前测试场景构造预设返回值,定义mock规则 ReturnData presetSuccessReturn = new ReturnData(); presetSuccessReturn.setCode(200); presetSuccessReturn.setMsg("success"); // 规则:当class2的targetMethod被调用(任意入参用any()匹配,也可以指定明确入参做精确匹配),返回预设对象 when(mockClass2.targetMethod(any())).thenReturn(presetSuccessReturn); // 其他依赖的返回规则按同样方式定义即可 // 第三步:把mock对象传入被测类构造器,创建被测实例 Class1 class1UnderTest = new Class1(mockClass2); // 第四步:执行被测方法 Class1ReturnType testResult = class1UnderTest.methodToBeTested(); // 第五步:按业务预期做断言即可 } @Test void testMethodToBeTested_ErrorScenario() { // 不同测试场景只需要修改预设的返回值即可,和其他测试用例完全隔离 Class2 mockClass2 = mock(Class2.class); ReturnData presetErrorReturn = new ReturnData(); presetErrorReturn.setCode(500); presetErrorReturn.setMsg("internal error"); when(mockClass2.targetMethod(any())).thenReturn(presetErrorReturn); Class1 class1UnderTest = new Class1(mockClass2); Class1ReturnType testResult = class1UnderTest.methodToBeTested(); // 对应异常场景的断言 } }
Spring上下文集成测试写法
如果需要启动Spring容器做集成测试,直接用@MockBean注解自动替换容器里的真实实例为mock对象即可:
import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.junit.jupiter.api.Test; import javax.annotation.Resource; @SpringBootTest public class Class1IntegrationTest { // 自动将Spring容器中Class2类型的Bean替换为mock对象 @MockBean private Class2 mockClass2; // 注入被测类,其依赖的Class2已经是上面的mock实例 @Resource private Class1 class1UnderTest; @Test void testSuccessScenario() { ReturnData presetReturn = new ReturnData(); presetReturn.setCode(200); when(mockClass2.targetMethod(any())).thenReturn(presetReturn); // 直接执行被测方法即可,不需要手动实例化Class1 Class1ReturnType result = class1UnderTest.methodToBeTested(); // 断言 } }
特殊场景处理
- 如果暂时无法改造被测类代码,被调用的是Class2的静态方法:使用Mockito 3.4.0+版本提供的
mockStatic能力即可,注意要写在try-with-resources块中自动释放mock:
try (MockedStatic<Class2> class2StaticMock = mockStatic(Class2.class)) { ReturnData presetReturn = new ReturnData(); presetReturn.setCode(200); class2StaticMock.when(() -> Class2.targetStaticMethod(any())).thenReturn(presetReturn); // 块内执行被测方法,静态调用会自动返回预设值 Class1ReturnType result = class1UnderTest.methodToBeTested(); }
- 如果方法内有多个依赖需要mock,只需要给每个依赖分别mock实例、定义对应场景的返回规则即可,不同测试用例的mock规则互不影响。
注意:如果业务方法内部直接通过
new Class2()创建依赖实例,任何mock方式都无法替换该实例,必须先改成依赖注入的写法才能正常mock。
内容的提问来源于stack exchange,提问作者src2012
相关产品推荐
相关产品推荐

