Home > Enterprise >  Mocking of a class method is not working in java
Mocking of a class method is not working in java

Time:07-03

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....
    }
 }
  • Related