Reputation:
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
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
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
Reputation: 588
There are several issues:
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.
@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