When I use @InjectMocks
, an exception was occurred. My code is shown below:
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 ...
}
it will throw an NPE, can someone help me?
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.
The problem is with your @InjectMocks
field. Since you did not initialize it directly like this:
@InjectMocks A a = new A("localhost", 80);
mockito will try to do constructor initialization. In this case it will choose the biggest constructor. In your case it's public A(String ip, int port)
. If no mock fields as provided that will match the constructor arguments, the mockito will pass null
s as a values for choosen constructor. So in this case the instance is initialized as new A(null, null)
. In this case you will get NPE
, since the second parameter of your constructor is int
, and when the null
will is going to be unboxed to int
the NPE
will be thrown.
What this exeception is telling you...
You haven't provided the instance at field declaration
In other words, you did not write...
@InjectMocks
A a = new A("foobar", 123);
This would be completely acceptable and will probably solve your problem. Please remember that mocks will NOT be initialized at that point, so it's fine if you really need an example String
and int
there, but not if you need to put mocks there. In other words, if you had a constructor that took an X and you would write new A(x)
here, x would be null, since the @Mock
annotation would not have been processed yet.
so I tried to construct the instance
Because there was no instance (because you didn't provide one) it tried to create one, but...
However, the constructor or the initialization block threw an exception: null
So, your constructor throws null. Seems like your someMethodCall
relies on the arguments (port, most likely) given not to be null, but since they are String
and int
, Mockito has no idea what values to use there. Since port
is a primitive type and Mockito does not handle those specifically, the problem is probably there - Mockito will try to put null there, which will throw an exception.
If your constructor matched X and Y, for example, Mockito would probably try to put the mocks there, but it doesn't. The constructor wants String
and int
and there are no mocks for them, so Mockito can only use default values and those are null
, which is a problem in case of port
(because of int
).
So, what's the solution?
1) Either make your constructor null-safe, allowing to give a null-port there (and make sure that the ip string is also handled in a null-safe way).
2) Use the thing you didn't use:
@InjectMocks
A a = new A("foobar", 123);
In any case, it is not required to have all the depedencies in the constructor, Mockito will do fine injecting them into fields directly. So adding another constructor for X and Y is not a real solution. Of course, generally, constructor injection is preferable over field injection, but that's another topic.
As for your question about which constructor: The documentation says this...
the biggest constructor is chosen, then arguments are resolved with mocks declared in the test only
Edit: Seems that Mockito does not know how to handle primitive fields in constructors, what a shame.
1) Either create a public non-args constructor or create public A(X x, Y y)
constructor.
2) Make sure you use
@RunWith(MockitoJUnitRunner.class)
public class ATest() {
or
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With