【问题标题】:Mockito @InjectMocks doesn't work for fields with same typeMockito @InjectMocks 不适用于相同类型的字段
【发布时间】:2015-07-09 05:39:43
【问题描述】:

我很惊讶地发现以下简单的代码示例不适用于所有 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 应该很简单,不是吗?

【问题讨论】:

    标签: java unit-testing mockito inject


    【解决方案1】:

    如果存在多个相同类型的模拟,这在模拟中记录为解决方法。它不会根据提供的名称解析实现(即@Mock(name = "b2"))。它用于解析实现的算法是注入依赖项的字段名称。所以上面的代码将正确解析(b2 => @Mock private B b2b3 => @Mock private B b3)。

    另一种解决方法是使用构造函数注入,这是注入依赖项的推荐方式。

    【讨论】:

      【解决方案2】:

      Mockito 似乎使用了their JavaDoc 中描述的算法

      如果我理解正确,它将首先按类型排序(在这种情况下只有 1 B),然后按名称排序(这里没有更改)。它最终将使用the OngoingInjector interface implementation 注入,这似乎是搜索第一个字段并注入它。

      由于您只定义了 1 个 B 并且 Mock 中有 2 个 B 字段,因此它将看到第一个实例与该字段的匹配并停止。这是因为mocks.size() == 1NameBasedCandidateFilter .因此它将停止过滤并直接注入。如果您创建多个相同类型的模拟,它们将按名称排序并相应地注入。

      当我创建多个特定类型的模拟(但少于字段数量)时,我能够让它工作。

      @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 应该非常不情愿地避免做坏事。此外,如果注入很复杂,那么您的对象要么太复杂,要么必须遵循另一种创建模式(例如 Joshua Bloch 构建器)
      • 这是一个已知问题,当被测系统中存在相同类型的模拟接口时,您的上述代码可以解决。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      相关资源
      最近更新 更多