@InjectMocks仅注入类的全局变量吗?为何Mock对象未成功注入到SomeClass中?
解答你的两个Mockito疑问
嘿,我来帮你把这两个问题捋清楚,咱们一个个来:
一、@InjectMocks仅会注入类的全局变量吗?
没错,@InjectMocks只能作用于类的成员变量(也就是你说的全局变量),不管这个变量是private、protected还是默认访问权限,它都能通过反射机制尝试注入。它的注入优先级大概是:
- 优先使用构造方法注入:如果被测试类有带依赖参数的构造方法,Mockito会把对应的Mock对象传入构造方法创建实例
- 其次是Setter方法注入:如果有对应依赖的Setter方法(比如
setSomeDao(SomeDao dao)),会调用Setter注入Mock - 最后是直接字段注入:如果前面两种都不满足,会直接通过反射替换类的成员变量值
但要注意,它完全管不到方法内部的局部变量——比如你第一种写法里someMethod()里new的SomeDao,@InjectMocks根本看不到这个变量,自然没法替换。
二、为什么你的Mock对象无法生效?
咱们针对你写的两种SomeClass写法分别分析:
1. 方法内部直接实例化依赖的情况
public class SomeClass { private void someMethod() { SomeDao someDao = new SomeDao(); // 这里是方法局部变量 someDao.retrieveSomeData(); } }
这里的someDao是someMethod()里的局部变量,完全是方法执行时才创建的新对象,和你测试类里用@Mock创建的someDao没有任何关系。Mockito没有能力干涉方法内部自己new出来的对象,所以调用的必然是真实方法。
2. 构造方法内实例化依赖的情况
public class SomeClass{ SomeDao someDao; public SomeClass() { someDao = new SomeDao(); // 构造方法里直接new真实实例 } private void someMethod() { someDao.retrieveSomeData(); } }
这里的问题有两个:
- 你在测试类里手动new了SomeClass实例:
private SomeClass someClass = new SomeClass();。当你手动调用无参构造方法时,已经把真实的SomeDao赋值给了成员变量。虽然@InjectMocks会尝试通过反射替换这个成员变量,但这种写法很容易出问题(比如反射替换时机晚于构造方法执行)。 - 更核心的是,
SomeClass自己在构造方法里硬编码创建了依赖,没有给外部替换依赖的入口——这违背了依赖注入原则,导致Mockito没法把Mock对象真正塞进去。
解决方案:修改依赖注入方式
要让Mock生效,你得让SomeClass的依赖可以从外部传入,而不是自己内部new。最推荐的是构造方法注入(因为它能保证依赖不可变,且实例创建时依赖就准备好):
修改SomeClass:
public class SomeClass{ private final SomeDao someDao; // 用带参数的构造方法接收外部传入的依赖 public SomeClass(SomeDao someDao) { this.someDao = someDao; } // 把方法改成public,不然测试里没法调用它 public void someMethod() { someDao.retrieveSomeData(); } }
修改测试类:
@ExtendWith(MockitoExtension.class) public class SomeClassTest { @Mock private SomeDao someDao; // 不要手动new,让Mockito通过构造方法注入Mock对象 @InjectMocks private SomeClass someClass; @Test void test() { // 定义Mock的返回逻辑 when(someDao.retrieveSomeData()).thenReturn(10); // 调用被测试方法 someClass.someMethod(); // 验证Mock方法是否被正确调用(这一步很重要,确保Mock生效了) verify(someDao).retrieveSomeData(); } }
这样修改后,Mockito会用你创建的Mock版SomeDao作为参数,调用SomeClass的构造方法创建实例,此时someClass里的someDao就是你定义的Mock对象,调用retrieveSomeData()时就会执行你设置的返回逻辑,而不是真实方法。
内容的提问来源于stack exchange,提问作者Wumba
相关产品推荐
相关产品推荐

