Issue
I have a Java service that consults a DB and returns a list, into my service I call the method validacionCampos() of my Util class, this class validates if a field of the list of my service has a null value or is 0 the value is set 0.000
My goal is to have a test case that covers all the service method and validations of the util class. I made a test clase but it doesn't work at all because the sonarqube tool says that it is not fully covered and points to the if lines of the util class, I made two methods, one setting the null value and another method for the value 0 , but I think it's wrong or does nothing
anyone can help me please, How is the testcase done so that it enters the service and the useful class and the coverage is fully?
ServiceImpl Class
@Override
public ResponseEntity<?> consultarReportes(Integer fechaInicio, Integer fechaFin) throws Exception {
Map<String, Object> response = new HashMap<>();
List<EntityDa> consultarReporte = new ArrayList<EntityDa>();
try {
consultarReporte = bitacoraRepository
.consultarBitacoras(fechaInicio, fechaFin);
reporteUtil.validacionCampos(consultarReporte);
} catch (Exception e) {
LOGGER.error("An error ocurred looking for Entity Data");
response.put("success", false);
response.put("error", e.getMessage());
return new ResponseEntity<Map<String, Object>>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
response.put("success", true);
response.put("data", consultarReporte);
return new ResponseEntity<>(response, HttpStatus.OK);
}
Util class
private static final String DECIMAL = "0.0000";
public List<EntityDa> validacionCampos(List<EntityDa> lista) {
for (EntityDa details : lista) {
if (details.getRango() == null || details.getRango().equals("0")) {
details.setRango(DECIMAL);
}
if (details.getPrima() == null || details.getPrima().equals("0")) {
details.setPrima(DECIMAL);
}
}
return lista;
}
Test class
@Mock
BitacoraRepository bitacoraRepository;
@Test
void testExitoValidaNullos() throws Exception {
EntityDa entity = new EntityDa();
entity.setRango(null);
entity.setPrima(null);
List<EntityDa> list = new ArrayList<>();
list.add(entity);
when(bitacoraRepository.consultarBitacoras("20220112", "20220112")).thenReturn(list);
}
@Test
void testExitoValidaVacios() throws Exception {
EntityDa entity = new EntityDa();
entity.setRango("0");
entity.setPrima("0");
List<EntityDa> list = new ArrayList<>();
list.add(entity);
when(bitacoraRepository.consultarBitacoras("20220112", "20220112")).thenReturn(list);
}
UPDATE
I IMPLEMENTED IT AS YOUR EXAMPLE AND IT STAYED LIKE THIS BECAUSE THE IDE ADDED A CAST TO ResponseEntity<Map<String, Object>> because my service is declarated ResponseEntity<?>
@Test
public void testExitoValidaNullos() throws Exception {
// Given a repository containing an entity with "rango" and "prima" set to null
EntityDa entity = new EntityDa();
entity.setRango(null);
entity.setPrima(null);
List<EntityDa> list = new ArrayList<>();
list.add(entity);
when(bitacoraRepository.consultarBitacoras("20220112", "20220112")).thenReturn(list);
// When consultarReportes is called
ResponseEntity<Map<String, Object>> response = (ResponseEntity<Map<String, Object>> serviceImpl.consultarReportes("20220112", "20220112");
// Then the returned entity should have "rango" and "prima" set to "0.0000"
EntityDa returnedEntity = ((List<EntityDa>) response.getBody().get("data")).get(0);
assertEquals("0.0000", returnedEntity.getRango());
assertEquals("0.0000", returnedEntity.getPrima());
}
I ran the test and it sends this:
org.opentest4j.AssertionFailedError: expected: <0.0000> but was: <41831.3>
apparently it is not taking the settings to null
UPDATE
TESTING THE UTIL CLASS DIRECTLY I GET THE ERROR NullPointerException in the line claseUtil.validacionCampos(list);
@AutoConfigureMockMvc
@SpringBootTest
public class ClaseTest {
@Autowired
ClaseUtil claseUtil;
@Test
public void prepare() throws Exception {
EntityDa entity = new EntityDa();
entity.setRango(null);
entity.setPrima(null);
List<EntityDa> list = new ArrayList<EntityDa>();
list.add(entity);
claseUtil.validacionCampos(list);
}
}
Solution
SonarQube reports no coverage for those lines because these tests do not actually execute this code. They perform some setup, but do not call any methods on ServiceImpl
.
It's useful to think of tests as having three sections, usually called given, when, and then:
- Given represents the context or environment of the test case
- When describes the action that is taken - the operation that is being tested
- Then describes the expected or intended outcome
It might be helpful to use written language to write out the requirements in this format, and then translate the written requirements to code.
For example, I might write the requirements for these test cases (in English):
- Given a repository containing an entity with a "rango" of
null
or "0", - When
consultarReportes
is called, - Then the returned entity should have "rango" set to "0.0000"
And:
- Given a repository containing an entity with a "prima" of
null
or "0", - When
consultarReportes
is called, - Then the returned entity should have "prima" set to "0.0000"
In terms of code:
- Given often translates to setting up data and sometimes mocks.
- When is a call to the method under test.
- Then is usually an assertion about the return value, or some other state affected by the method. It can also be verifications of methods called on mock objects, particularly when you are testing a method with no return value that only performs side effects.
In this case, the tests might look like this:
@Test
public void testExitoValidaNullos() throws Exception {
// Given a repository containing an entity with "rango" and "prima" set to null
EntityDa entity = new EntityDa();
entity.setRango(null);
entity.setPrima(null);
List<EntityDa> list = new ArrayList<>();
list.add(entity);
BitacoraRepository bitacoraRepository = mock(BitacoraRepository.class);
when(bitacoraRepository.consultarBitacoras("20220112", "20220112")).thenReturn(list);
// When consultarReportes is called
ServiceImpl serviceImpl = new ServiceImpl(bitacoraRepository);
ResponseEntity<Map<String, Object>> response = serviceImpl.consultarReportes("20220112", "20220112");
// Then the returned entity should have "rango" and "prima" set to "0.0000"
EntityDa returnedEntity = ((List<EntityDa>) response.getBody().get("data")).get(0);
assertEquals("0.0000", returnedEntity.getRango());
assertEquals("0.0000", returnedEntity.getPrima());
}
The change to testExitoValidaVacios would be similar.
(I made a few other changes to the supplied code to allow it to compile.)
To fully cover all possibilities, you would also want to write test cases for when these fields are set to other values. You may also want to test the exception handler by stubbing bitacoraRepository.consultarBitacoras
to throw an exception.
Note that it would probably be simpler to test these cases against the Util
class directly. This would require much less setup (no need for mocks) and would not require unpacking the entity from the response.
Answered By - Tim Moore
Answer Checked By - Cary Denson (JavaFixing Admin)