@InjectMocks,构造函数或初始化块抛出异常

din*_*rui 9 java junit unit-testing mockito

当我使用时@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 ...
}
Run Code Online (Sandbox Code Playgroud)

它会抛出 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.

Flo*_*etz 10

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

您尚未在字段声明中提供实例

换句话说,你没有写...

@InjectMocks 
A a = new A("foobar", 123);
Run Code Online (Sandbox Code Playgroud)

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

所以我试图构建实例

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

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

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

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

那么,有什么解决办法呢?

1)要么让你的构造函数为空安全,允许在那里提供一个空端口(并确保ip字符串也以空安全的方式处理)。

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

@InjectMocks 
A a = new A("foobar", 123);
Run Code Online (Sandbox Code Playgroud)

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

至于你关于哪个构造函数的问题:文档说这个......

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

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


Ser*_*hyr 9

问题出在你的@InjectMocks领域。由于您没有像这样直接初始化它:

@InjectMocks A a = new A("localhost", 80);
Run Code Online (Sandbox Code Playgroud)

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


Mac*_*ski 2

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

2)确保您使用

@RunWith(MockitoJUnitRunner.class)
public class ATest() {
Run Code Online (Sandbox Code Playgroud)

或者

@Before
public void init(){
  MockitoAnnotations.initMocks(this);
}
Run Code Online (Sandbox Code Playgroud)