Mockito @InjectMocks 不适用于相同类型的字段
Mockito @InjectMocks doesn't work for fields with same type
我很惊讶地发现以下简单代码示例并不适用于所有 > 1.8.5 的 Mockito 版本
@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) 中有一句话:
Note 1: If you have fields with the same type (or same erasure), it's
better to name all @Mock annotated fields with the matching fields,
otherwise Mockito might get confused and injection won't happen.
这是否意味着如果我有多个具有相同类型的字段我不能只模拟其中一个而应该为具有相同类型的 ALL 字段定义 @Mock
类型?
它是已知的限制吗?是否有任何原因尚未修复?
按字段名称匹配 @Mock
应该很简单,不是吗?
Mockito 似乎使用了 their JavaDoc
中描述的算法
如果我没理解错的话,它会先按类型排序(在本例中只有 1 B),然后按名称排序(这里没有变化)。它最终将使用 the OngoingInjector interface implementation 注入,这似乎是搜索第一个字段并注入它。
因为你只定义了1个B,Mock中有2个B的字段,它会看到第一个实例与该字段的匹配并停止。这是因为 mocks.size() == 1
在 NameBasedCandidateFilter
.因此它将停止过滤并直接注入。如果您创建多个相同类型的模拟,它们将按名称排序并相应地注入。
当我创建多个特定类型的模拟(但少于字段数)时,我能够让它工作。
@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 问题,以增强或更改注入过滤算法,以便进行查看。
这在 mockito 中记录为解决方法,如果存在多个相同类型的模拟。它不会根据提供的名称解析实现(即 @Mock(name = "b2")
)。它用于解析实现的算法是通过注入依赖项的字段名称。所以您上面的代码将正确解析(b2
=> @Mock private B b2
和 b3
=> @Mock private B b3
)。
另一种解决方法是使用构造函数注入,这是注入依赖项的推荐方法。
我很惊讶地发现以下简单代码示例并不适用于所有 > 1.8.5 的 Mockito 版本
@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) 中有一句话:
Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.
这是否意味着如果我有多个具有相同类型的字段我不能只模拟其中一个而应该为具有相同类型的 ALL 字段定义 @Mock
类型?
它是已知的限制吗?是否有任何原因尚未修复?
按字段名称匹配 @Mock
应该很简单,不是吗?
Mockito 似乎使用了 their JavaDoc
中描述的算法如果我没理解错的话,它会先按类型排序(在本例中只有 1 B),然后按名称排序(这里没有变化)。它最终将使用 the OngoingInjector interface implementation 注入,这似乎是搜索第一个字段并注入它。
因为你只定义了1个B,Mock中有2个B的字段,它会看到第一个实例与该字段的匹配并停止。这是因为 mocks.size() == 1
在 NameBasedCandidateFilter
.因此它将停止过滤并直接注入。如果您创建多个相同类型的模拟,它们将按名称排序并相应地注入。
当我创建多个特定类型的模拟(但少于字段数)时,我能够让它工作。
@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 问题,以增强或更改注入过滤算法,以便进行查看。
这在 mockito 中记录为解决方法,如果存在多个相同类型的模拟。它不会根据提供的名称解析实现(即 @Mock(name = "b2")
)。它用于解析实现的算法是通过注入依赖项的字段名称。所以您上面的代码将正确解析(b2
=> @Mock private B b2
和 b3
=> @Mock private B b3
)。
另一种解决方法是使用构造函数注入,这是注入依赖项的推荐方法。