Search code examples
spring-bootcontrollermockitojunit4junit5

Mocking two different implementation of an Interface in Controller Mockito Junit test case


I have an interface(Client) with two implementation classes PClient and SClient respectively.

Interface  - Client
Impl class - PCleint, SClient

I have a controller in which both the impl classes are autowired using qualifier spring boot annotation. Now i need to write JUNIT mockito test case for the controller.

I am not sure how to mock the different Impl classes which has the same interface. I tried mocking in the following way but it throws null pointer exception in the stub method of the respective mocks.

@Mock
private Client pclient

@Mock 
private Client sclient

How do i differentiate mocking different impl classes such that the methods of the impl classes are called. Should i do like the below.

@Mock
private PClient pclient

@Mock 
private SClient sclient

It did not work for me . Either one throws null pointer exception . Please advise.


Solution

  • I guess the tricks are you need to use Before to stub the return value and use @InjectMocks for the controller. Since @Mock won't return a "real" object, it just return a "mock"(or fake:P ) one. So when you call a method of this "mock" object, it won't return the actual output from your method, instead it gives back null. That's why you need to stub the return with @Before.

    See my example code below:

    Client Interface

    public interface Client {
        String someMethod();
    }
    

    Class implemented

    public class PClient implements Client {
        private static final String name = "PClient";
        @Override
        public String someMethod() {
            return name;
        }
    }
    
    
    public class SClient implements Client{
        private static final String name = "SClient";
        @Override
        public String someMethod() {
            return name;
        }
    }
    

    The Client Controller

    @Controller public class ClientController {

    @Autowired
    private PClient pClient;
    @Autowired
    private SClient sClient;
    
    public ClientController(){
    }
    public String executePClient(){
        return this.pClient.someMethod();
    }
    
    public String executeSClient(){
        return this.sClient.someMethod();
    }
    
    public String execute(Client client){
        return client.someMethod();
    }
    

    }

    The test case You need to add a @Before to stub the method return.

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.MockitoJUnitRunner;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Mockito.when;
    
    @RunWith(MockitoJUnitRunner.class)
    public class ClientControllerTest {
        @Mock
        private PClient pclient;
        @Mock
        private SClient sclient;
        @InjectMocks
        private ClientController clientController;
    
        @Before
        public void setup(){
            when(pclient.someMethod()).thenReturn(new PClient().someMethod());
            when(sclient.someMethod()).thenReturn(new SClient().someMethod());
        }
    
        @Test
        public void testPClient(){
            String result = clientController.executePClient();
            assertEquals("PClient",result);
        }
    
        @Test
        public void testSClient(){
            String result = clientController.executeSClient();
            assertEquals("SClient",result);
        }
    
        @Test
        public void testExecuteWithPClient(){
            String result = clientController.execute(pclient);
            assertEquals("PClient",result);
        }
    
        @Test
        public void testExecuteWithSClient(){
            String result = clientController.execute(sclient);
            assertEquals("SClient",result);
        }
    }
    

    Result: Tests passed: 4 of 4 tests - 31ms