So I'm trying to test my api and its giving me a nullpointer exception when I try to use the repository.
Supposedly I have a h2 database for testing in my application.yml(inside test/resources) like this:
spring:
profiles:
active: test
datasource:
username:
password:
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;
platform: h2
Here is my ControllerAPItest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureTestDatabase
class ControllerAPITest {
@Mock
CompaniaRepository companiaRepository;
@Mock
DefaultGroupRepository defaultGroupRepository;
@Mock
OfficeRepository officeRepository;
@InjectMocks
CompaniaServiceImpl companiaService;
ControllerAPI controllerAPI = new ControllerAPI(companiaService);
@BeforeEach
void setUp() {
Compania compania = new Compania();
compania.setName("companiaTest");
compania.setDominio("dominioTest");
compania.setAltas("altasTest");
compania.setBajas("bajasTest");
compania.setDefault_group(null);
compania.setOffice(null);
companiaRepository.save(compania);
}
@AfterEach
void tearDown() {
companiaRepository.deleteAll();
}
@Test
void getByField() {
}
@Test
void getCompanias() {
System.out.println(controllerAPI.GetCompanias());
}
}
And here is the output of the error I'm getting:
java.lang.NullPointerException at controller.ControllerAPI.GetCompanias(ControllerAPI.java:114) at controller.ControllerAPITest.getCompanias(ControllerAPITest.java:65)
It is pointing to the line that says:
companiaRepository.save(compania);
Here is the full Error: https://textdoc.co/eGmu72wa1ydEoJ4T
What is happening with my code?
Thanks!
Edit. here is my getCompanias in my controller.
@ResponseStatus(HttpStatus.OK)
@GetMapping()
public Map<String, List<Compania>> GetCompanias() {
Map<String, List<Compania>> mappedResult = Collections.singletonMap("result", companiaService.getCompanias());
return mappedResult;
// return companiaService.getCompanias();
}
That uses the companiaService here:
public List<Compania> getCompanias() {
return companiaRepository.findAll();
}
Thanks!
CodePudding user response:
The test setup has a lot of issues, and my suggestion would be to read through the mockito and spring test basics, for instance here and here .
Test setup
For starters, the code mixes up different test setups. The annotation @RunWith(SpringRunner.class)
suggests a JUnit4 setup, but the strack trace indicates the use of JUnit5, as the jupiter package is on the classpath (org.junit.jupiter.engine.execution....
). The @RunWith
can be removed as JUnit5 relies on extensions, and the spring extention (@ExtendWith(SpringExtension.class)
) is already present as a meta annotation in the @SpringBootTest
annotation (source).
Use of mock annotations
When you plan to create a Spring boot test, mocks can be injected in the TestApplicationContext using the @MockBean
annotation. However, by using @Mock
together with @InjectMocks
, the mocks will be created by Mockito only, without being registered in the application context. More info on the differences can be found here.
Mock preparation
Mocks are "fake" implementations of classes, and are used to mimic the behaviour of a real object. Its not a real object and it only performs the tasks you have assigned to it. In the setup() however, the method attempt to access the mock as a real object. This won't work as its not the correct way to set up mock:
// The CompaniaRepository is annotated as being a mock
@Mock
CompaniaRepository companiaRepository;
...
@BeforeEach
void setUp() {
Compania compania = new Compania();
...
// Here a method on the mock is invoked, which has no use
companiaRepository.save(compania);
}
More info on setting up a Mockito mock can be found here.
Furthermore, if you plan to set up a database test I wouldn't suggest on mocking the repositories as it diminishes the requirement of using a database (assuming there isn't a third repository which does require H2).
A final thing; Spring controllers are best tested by performing tests via the web layer. Instantiating the controller under test using new ControllerApi(...)
will only test the methods, and not the endpoints they are mapped to. A great tutorial on how to test the web layer can be found here.