【问题标题】:@InjectMocks, the constructor or the initialization block threw an exception@InjectMocks,构造函数或初始化块抛出异常
【发布时间】:2017-11-24 02:35:05
【问题描述】:

当我使用@InjectMocks 时,发生了异常。我的代码如下所示:

class A {
    private X x;
    private Y y;
    public A(String ip, int port) {
       this(someMethodCall(ip, port)); //
    }

    private A(X x) {
        this.x = x;
        this.y = new Y();
    }
}
UT:
public class ATest() {
    @InjectMocks A a;
    @Mock X x;
    @Mock Y y;
    @Test ...
}

它会抛出一个 NPE,有人可以帮助我吗?

org.mockito.exceptions.base.MockitoException: Cannot instantiate @InjectMocks field named 'channel' of type 'class Juinit3.Channel'. You haven't provided the instance at field declaration so I tried to construct the instance. However, the constructor or the initialization block threw an exception: null.

【问题讨论】:

    标签: java unit-testing junit mockito


    【解决方案1】:

    这个异常告诉你什么...

    您没有在字段声明时提供实例

    也就是说,你没有写……

    @InjectMocks 
    A a = new A("foobar", 123);
    

    这是完全可以接受的,并且可能会解决您的问题。请记住,那时模拟不会被初始化,所以如果你真的需要一个例子 Stringint 那里很好,但如果你需要在那里放置模拟则不需要。换句话说,如果您有一个带 X 的构造函数,并且您将在此处编写新的 A(x),则 x 将为 null,因为尚未处理 @Mock 注释。

    所以我尝试构建实例

    因为没有实例(因为你没有提供一个)它试图创建一个,但是......

    但是构造函数或者初始化块抛出了异常:null

    因此,您的构造函数会抛出 null。似乎您的someMethodCall 依赖于不为空的参数(端口,很可能),但由于它们是Stringint,Mockito 不知道在那里使用什么值。由于port 是原始类型,而 Mockito 没有专门处理这些,问题可能就在那里 - Mockito 会尝试将 null 放在那里,这将引发异常。

    如果您的构造函数匹配 X 和 Y,例如,Mockito 可能会尝试将模拟放在那里,但它没有。构造函数需要Stringint 并且没有针对它们的模拟,因此Mockito 只能使用默认值并且那些是null,这是port 的问题(因为int)。

    那么,解决办法是什么?

    1) 使您的构造函数为空安全,允许在此处提供空端口(并确保 ip 字符串也以空安全的方式处理)。

    2) 使用你没用过的东西:

    @InjectMocks 
    A a = new A("foobar", 123);
    

    在任何情况下,都不需要在构造函数中拥有所有依赖项,Mockito 可以直接将它们注入到字段中。因此,为 X 和 Y 添加另一个构造函数并不是真正的解决方案。当然,一般来说,构造函数注入比字段注入更可取,但这是另一个话题。

    至于您关于哪个构造函数的问题:documentation 这么说...

    选择最大的构造函数,然后使用仅在测试中声明的模拟来解析参数

    编辑:似乎 Mockito 不知道如何处理构造函数中的原始字段,真可惜。

    【讨论】:

    【解决方案2】:

    问题在于您的 @InjectMocks 字段。由于您没有像这样直接初始化它:

    @InjectMocks A a = new A("localhost", 80);
    

    mockito 将尝试进行构造函数初始化。在这种情况下,它将选择最大的构造函数。在您的情况下,它是public A(String ip, int port)。如果没有提供与构造函数参数匹配的模拟字段,则模拟将传递 nulls 作为所选构造函数的值。所以在这种情况下,实例被初始化为new A(null, null)。在这种情况下,您将获得NPE,因为您的构造函数的第二个参数是int,当null 将被拆箱到int 时,NPE 将被抛出。

    【讨论】:

      【解决方案3】:

      1) 创建一个公共的非参数构造函数或创建public A(X x, Y y) 构造函数。

      2) 确保使用

      @RunWith(MockitoJUnitRunner.class)
      public class ATest() {
      

      @Before
      public void init(){
        MockitoAnnotations.initMocks(this);
      }
      

      【讨论】:

      • 我想问另一个问题。实例化 @InjectMocks 字段时将调用哪个构造函数?有什么命令吗?我试过带参数的构造函数和不带参数的构造函数,但我发现调用似乎没有规则。
      • 这里的 2) 不是问题,因为如果这些不存在,没有人会对 @InjectMocks 做任何事情,因此不会出现这样的异常,所以我们可以推断出其中之一有两个。
      • 另外问题不在于没有将 x 和 y 提供给构造函数,因为 Mockito 可以很好地与字段注入一起工作,因此 1) 的第二部分也不能成为解决方案。虽然默认构造函数可以工作,但我不会添加一个只是为了测试。
      猜你喜欢
      • 2013-07-07
      • 2011-02-09
      • 2012-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多