我非常惊讶地发现,下面的简单代码示例并不适用于所有Mockito 1.8.5 版本
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@InjectMocks
private A a;
@Test
public void testInjection() throws Exception {
assertNotNull(a.b2); //fails
assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here
}
static class A{
private B b1;
private B b2;
}
interface B{}
}
在javadocs中(http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html)有一句话:
注意1:如果您有相同类型(或相同擦除)的字段最好用匹配字段命名所有@Mock注释字段,否则Mockito可能会感到困惑,注射也不会发生。
这是否意味着,如果我有多个类型相同的字段,我不能只模拟其中一个,而是应该为ALL具有相同类型的字段定义@Mock
?这是已知的限制吗?还有什么原因没有解决吗?通过字段名称匹配@Mock
应该很简单,不是吗?
Mockito似乎使用了JavaDoc 中描述的算法
如果我理解正确,它将首先根据类型排序(在本例中仅为1B),然后根据名称排序(此处没有更改)。它最终将使用OngoingInjector接口实现进行注入,该接口似乎搜索第一个字段并注入它
由于您只定义了1个B,并且Mock中有2个B字段,因此它将看到第一个实例与字段的匹配并停止。这是因为NameBasedCandidateFilter中的mocks.size() == 1
。因此,它将停止过滤并直接注入。如果您创建了多个相同类型的mock,它们将根据Name进行排序并相应地注入。
当我创建了多个特定类型的mock(但少于字段的数量)时,我就可以让它工作了。
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@Mock(name = "b3")
private B b3;
@InjectMocks
private A a;
@Test
public void testInjection() {
System.out.println(this.a);
}
static class A {
private B b1;
private B b2;
private B b3;
}
interface B {
}
}
这将正确地将b2注入a.b2,并将b3注入a.b3,而不是a.b1和a.b2(a中定义的前2个字段)。
你总是可以在他们的存储库中留下GitHub问题,并对注入过滤算法进行增强或更改,以便查看。
如果存在同一类型的多个mock,这在mockito中被记录为解决方案。它不会根据提供的名称(即@Mock(name = "b2")
)解析实现。它用来解析实现的算法是通过注入的依赖项的字段名。因此,上面的代码将正确解析(b2
=>@Mock private B b2
和b3
=>@Mock private B b3
)。
另一种解决方法是使用构造函数注入,这是注入依赖关系的推荐方式。