user15334991
user15334991

Reputation:

Null Pointer Exception JUnit Mockito

I m new to JUnit and Mockitio. When I run the below code, I get, java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getStatusCodeValue()" because the return value of "com.learnit.testing.MyController.getUser(java.lang.Long)" is null

@Controller
@RequestMapping("/api")
public class MyController {

    @Autowired
    private MyService service;

    @GetMapping("/")
    @ResponseBody
    public ResponseEntity<Object> getUser(Long id) throws Exception {
        return service.myResponse(id);
    }
}

@Service
public class MyService {
    
    @Autowired
    private MyRepository repository;

    public ResponseEntity<Object> myResponse(Long id) throws Exception{

        MyData data=repository.findById(id).orElse(null);
        if(data!=null)
            return new ResponseEntity<>(HttpStatus.OK);
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

    }
}

Testing

@ExtendWith(MockitoExtension.class)
public class MyTest {

    @InjectMocks
    private MyController controller;
    
    @Mock
    private MyRepository repo;
    
    @Mock
    private MyService service;
    
    @Test
    public void checkService() throws Exception {
        when(repo.findById((long)1)).thenReturn(null);
        assertEquals(controller.getUser((long)1).getStatusCodeValue(), 500);
        
    }
}

Upvotes: 1

Views: 13390

Answers (3)

Kirill Feoktistov
Kirill Feoktistov

Reputation: 1448

NullPointerException indicates that the mocked objects were not initialised, which means that @ExtendWith(MockitoExtension.class) annotation was not invoked. It is possible to fix either by enabling JUnit annotations or initialise the mocks manually.

Enabling JUnit 5 runtime annotations

To make the annotation work junit-jupiter-engine dependency should be added.

Initialising mocks manually

If for any reasons adding an additional test dependency is impossible, the mocks can be initialised in the following way, attention to mocks object in the following code:

public class MyTest {

    @InjectMocks
    private MyController controller;
    
    @Mock
    private MyRepository repo;
    
    @Mock
    private MyService service;

    private AutoClosable mocks;

    @BeforeEach
    private void setUp() {
        mocks = MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    private void tearDown() {
        mocks.close();
    }
    
    @Test
    public void checkService() throws Exception {
        when(repo.findById((long)1)).thenReturn(null);
        assertEquals(controller.getUser((long)1).getStatusCodeValue(), 500);
        
    }
}

If mocks are initialised manually, @ExtendWith(MockitoExtension.class) annotation can be omitted.

Upvotes: 0

Luis Colorado
Luis Colorado

Reputation: 12645

IMHO you need to add a @RunWith(some.mockito.TestSuiteIDontRememberName.class) annotated to the test class. The @Mock annotation requires a test suite that searches for the annotation and gathers the information to inject the values there.

If I were you, I'd first try to initialize the mocked instances in a @Before method and see if the test runs... then, I'd read the Mockito (sorry, but I normally use EasyMock) library documentation, and check how is the @Mock annotation used (I can tell you you have chances for this to be the problem)

And there's a problem in your test code... you are telling the mocked instance to return null when calling method findById(), so most probably if that was to happen, you'll get a NPE in the main code, when trying to use the .orelse() method. Probably you don't know what to return, or you need to return an also mocked object instance... but as such, it is impossible to repair your code without implementing something completely different to your intention.

Anyway, you have eliminated enough things in your code to make it untestable, and in this process you have not included how do you call the method to be tested at all.

Please, read the page How to create a Minimal, Reproducible Example and edit your question to include testable code, showing that NPE, so we can, at least, see how and where it is occuring.

Upvotes: 0

Marcin Rzepecki
Marcin Rzepecki

Reputation: 588

There are several issues:

  1. You did not stub the behaviour of the MyService#myResponse method. You need to mock the MyService instance as this is the direct dependency of the MyController instance. Therefore, the MyRepository don't needs to be mocked in this test case (unless you want to stub its methods for some reason, but I don't it in the current code). I would recommend you to replace the following:
        when(repo.findById((long)1)).thenReturn(null);

with stubbing behaviour of the MyService mock instance:

        when(service.myResponse(1L)).thenReturn(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));

When you do this, the MyRepository test suite mock instance can be removed, as it is not required anymore.

  1. Check your imports, based on the usage of @ExtendWith annotation I assume you are using JUnit 5 here. Make sure that you actually use other classes from org.junit.jupiter.api(for example @Test and Assertions). By default, the JUnit does not support backward compatibility with the JUnit 4. Still, I you want to keep the compatibility you should include the junit-vintage-engine artifact in your test runtime path.

P.S You need to think which class you actually want to test, that determines which instances should be mocked.

Upvotes: 2

Related Questions