如何Mock类内部未作为参数传入的方法?Java Mockito测试答疑
问题:Mock非依赖注入的OtherClass实例是否生效?
业务代码(MyClass)
import name.package.OtherClass; import name.package.Context; import name.package.Configuration.keys; public class MyClass{ public static void configure(Context context){ Configuration config = new OtherClass().getConfiguration(keys); context.configure(config); } }
测试类(MyClassTest)
import org.mockito.Mock; import org.mockito.Mockito; public class MyClassTest{ @Mock private Context context = Mockito.mock(Context.class); @Mock private OtherClass otherClass = Mockito.mock(OtherClass.class); @Mock private Context configurationMock = Mockito.mock(Configuration.class); @Test public void testConfigure(){ Mockito.when(otherClass.getConfiguration(keys)).thenReturn(configurationMock); MyClass.configure(context); verify(context.configure(configurationMock), times(1)) } }
核心疑问:测试类中Mock的OtherClass实例,能否对MyClass里new OtherClass().getConfiguration()的调用生效?
解答
1. 直接结论
你的Mock完全不会生效,因为MyClass中是通过new OtherClass()硬编码创建实例,和测试类里Mock的otherClass是两个完全独立的对象,Mockito无法拦截这种直接new出来的对象调用。
2. 问题根源
Mockito的Mock逻辑仅对被注入、传递到目标类的实例生效,但你的业务代码里没有任何依赖注入逻辑——运行时会自己创建全新的OtherClass实例,和测试类里的Mock实例毫无关联,所以你设置的when(otherClass.getConfiguration(keys))不会对业务代码产生任何影响。
3. 修复方案
方案一:重构业务代码,依赖注入OtherClass
修改MyClass的configure方法,将OtherClass作为参数传入,让测试时可以直接传递Mock实例:
public class MyClass{ public static void configure(Context context, OtherClass otherClass){ Configuration config = otherClass.getConfiguration(keys); context.configure(config); } }
对应的测试代码调整:
@Test public void testConfigure(){ Mockito.when(otherClass.getConfiguration(keys)).thenReturn(configurationMock); // 传入Mock的otherClass MyClass.configure(context, otherClass); // 修正verify的写法 Mockito.verify(context, Mockito.times(1)).configure(configurationMock); }
方案二:使用字节码增强工具拦截new操作
如果无法修改业务代码,可以用PowerMock或Mockito的inline mock maker来拦截new OtherClass()的操作,强制返回Mock实例:
以PowerMock为例,测试类调整如下:
import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(PowerMockRunner.class) // 告诉PowerMock要拦截MyClass中的new操作 @PrepareForTest(MyClass.class) public class MyClassTest{ @Mock private Context context; @Mock private OtherClass otherClass; @Mock private Configuration configurationMock; @Test public void testConfigure(){ // 拦截new OtherClass(),返回Mock实例 PowerMockito.whenNew(OtherClass.class).withNoArguments().thenReturn(otherClass); Mockito.when(otherClass.getConfiguration(keys)).thenReturn(configurationMock); MyClass.configure(context); Mockito.verify(context, Mockito.times(1)).configure(configurationMock); } }
注意:PowerMock会修改字节码,需注意与JDK、Mockito的版本兼容性;Mockito 3.4+的inline mock maker也支持类似功能,配置方式略有不同。
4. 测试代码的额外问题
@Mock注解和Mockito.mock()重复,二者选其一即可,同时需确保测试类被Mockito处理器接管(比如添加@RunWith(MockitoJUnitRunner.class)或使用MockitoExtension)。verify写法错误,正确格式是Mockito.verify(被验证对象, 次数).方法(参数),你当前的写法会直接调用方法而非验证调用次数。
内容的提问来源于stack exchange,提问作者emanoellucas
相关产品推荐
相关产品推荐

