unit-testingjunitmockito

Mockito junit testing not working- beans mocked by @mockBean are null


I am trying Mockito for mocking and unit testing.

Trying to mock an autowired bean using @MockBean. But the bean is null at run time.

Class under test.

@Service
public class UserServiceImpl  {

  @Autowired
  GenericRestClient restClient;

  @Autowired
  RequestMapper requestMapper;

  @Autowired
  ResponseMapper responseMapper;

  @Override
  public List<User> findUsers(RRequest requestBody,
      String index) throws HostException {
List<User> users= 
requestMapper.mapRequest("test");
// ...doing something
return users;
}

The test class :


import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.mock.mockito.MockBean;

@RunWith(MockitoJUnitRunner.class)

public class UserServiceImplTest {

    @MockBean
    GenericRestClient restClient;

    @MockBean
    RequestMapper requestMapper;

    @MockBean
    ResponseMapper responseMapper;

    @InjectMocks
    UserServiceImpl userService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);

    }

    @Test
    public void testFindUsers() {
List<Users> users = null;
        Mockito.when(requestMapper.mapRequest("test"))
        .thenReturn(users); 
        assertNull(users);      
    }
}

Error :

java.lang.NullPointerException
    at test.my.code.UserServiceImplTest.testFindUsers(UserServiceImplTest.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)


I have debugged, NullPointerException is because of requestMapper (mocked using MockBean as it is null.)

What is wrong with this code?


Solution

  • Unlike @abinmichael I don't suggest using Spring Runner to run this test. Your test is a unit test, and running Spring (creating an application context) is a very expensive operation, more suitable for integration testing.

    I'm not a mockito expert, but I believe, you should refactor slightly the UserServiceImpl so that the dependencies will become visible:

    @Service
    public class UserServiceImpl  {
    
         private final GenericRestClient restClient;
         private final RequestMapper requestMapper;
         private final ResponseMapper responseMapper;
    
         @Autowired // in recent spring version, this annotation can be omitted 
         public UserServiceImpl(GenericRestClient restClient, RequestMapper requestMapper, ResponseMapper responseMapper) {
            this.restClient = restClient;
            this.requestMapper = requestMapper;
            this.responseMapper = responseMapper;    
    }
    ...
    

    With such an approach there is no need in @InjectMocks anymore:

    @RunWith(MockitoJUnitRunner.class)
    public class UserServiceImplTest {
    
        @Mock
        GenericRestClient restClient;
    
        @Mock
        RequestMapper requestMapper;
    
        @Mock
        ResponseMapper responseMapper;
    
        
        UserServiceImpl userService;
    
        @Before 
        public void init() {
          userService = new UserServiceImpl(restClient, requestMapper, responseMapper);
        }
    
    ...
    }
    

    If you insist on using fields injection, read The accepted answer to get more information about how does @InjectMocks work. Maybe you don't have "private" access modifier, maybe you have some mix between Constructor injection and field injection and @InjectMocks doesn't not support both simultaneously