Attached the code and it's test below :
public class A {
private B b;
public A(C c) {
b = new B(c);
}
public ResponseOutput method1(RequestInput request) {
ResponseOutput responseOutput = b.method2(param1, param2);
//Do something based on responseOutput. e.g. throwException if some condition meet
return responseOutput;
}
}
public class ATest {
@Mock
private C c;
@Mock
private B b;
@InjectMocks
private A a;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void method1_test1() {
ResponseOutput responseOutput = something;
Mockito.when(b.method2(Mockito.any(), Mockito.any())).thenReturn(responseOutput);
a.method1(param1, param2);
}
}
b.method2 is not getting mocked. Call is going to it's internal functions. Is there any other annotation needed for mocking class B.
CodePudding user response:
First, @InjectMocks will create the mock object (creating a new object every time an @Test method is run)
However, you are not using that created instance - instead you are creating a new instance directly in your @Before method. @InjectMocks has no effect on that created instance.
So you need to decide, do you want to use the @InjectMocks created instance of A, or do you you want to use your own (in which case, you must set b yourself - maybe using PowerMock to mock the new call, or alternatively use ideas from Mocking new instance creation inside testing class using mockito ).
In addition, @InjectMocks will still not set b for you, because Java initialises objects in order of declaration, which results in @InjectMocks only being able to inject @Mock variables declared before it - so you would have to switch the ordering around to have @InjectMocks AFTER the @Mock declarations.
CodePudding user response:
@InjectMock
will try to use the biggest constructor to inject the mocked dependencies into A .(Refer this for detail of its behaviour) which in your case is :
A a = new A(c);
Now you manually create a B instance inside this constructor which causes B inside A is a real instance but not a mocked one. So what you stub on the mocked B does not have any effect because A is not calling it.
Actually , A only needs to directly interact with B and so you do not need to mock C . First refactor A such that it can be directly created using B only :
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
And then update the test to :
public class ATest {
@Mock
private B b;
@InjectMocks
private A a;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void method1_test1() {
}
}
Please note that you have to remove the constructor public A(C c)
from A. Otherwise , @InjectMocks
will just randomly choose public A(C c)
or public A(B b)
to create A because both are the biggest constructors.
If you do not want to remove constructor public A(C c)
, don't use @InjectMocks
to create A but simply create it by yourself :
public class ATest {
@Mock
private B b;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void method1_test1() {
A a = new A(b);
//do your test....
}
}