INTERFACE : JpaRepository -- METHODE : findById -- EXCEPTION NON LANCEE
Bonjour à tous,
je vous fais part d'un comportement inattendu que je rencontre dans un projet JAVA :
voici la description en détails de ce cas inattendu :
(01.)Je créée un projet JAVA :
- Dans le fichier de configuration 'pom.xml' : J'intègre spring-boot, spring-data et le connecteur à une BDD de type MariaDB.
- Dans le fichier de propritétés 'application.yml' : J'intègre la connexion à ma BDD.
(02.)Je créée une entité 'Specie' :
Cette entité 'Specie' représentera une espèce (animale ou végétale), avec son id, son nom latin, et son nom commun.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Entity
public class Specie {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(unique=true)
private String latinName;
private String commonName;
/**
* <b>CONSTRUCTEUR</b><br/>
* <b>ARGUMENTS : AUCUN</b><br/>
*/
public Specie() {}
/**
* <b>CONSTRUCTEUR</b><br/>
* <b>ARGUMENTS : TOUS LES ATTRIBUTS (L'ATTRIBUT 'id' INCLUS)</b><br/>
*
* @param pId L'attribut 'id' de l'entité.
* @param pLatinName L'attribut 'latinName' de l'entité.
* @param pCommonName L'attribut 'commonName' de l'entité.
*/
public Specie(Long pId, String pLatinName, String pCommonName) {
...
}
/**
* <b>CONSTRUCTEUR</b><br/>
* <b>ARGUMENTS : TOUS LES ATTRIBUTS (L'ATTRIBUT 'id' EXCLUS)</b><br/>
*
* @param pLatinName L'attribut 'latinName' de l'entité.
* @param pCommonName L'attribut 'commonName' de l'entité.
*/
public Specie(String pLatinName, String pCommonName) {
...
}
} |
(03.)Je créé un DAO 'SpecieRepository' :
Ce DAO effectuera les opérations de persistance (fonctionnalités CRUD et autres) relatives à l'entité 'Spécie'.
Code:
1 2 3 4
| @Repository
public interface SpecieRepository extends JpaRepository<Specie, Long> {
...
} |
(04.)Je créé une interface 'SpecieService' :
Cette interface 'SpecieService' exposera des traitements métier relatifs à l'entité 'Specie'.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface SpecieService extends Service<Specie, Exception> {
/**
* <b>RECHERCHER UNE ENTITE DANS LES DONNEES PERSISTANTES.</b><br/>
* CRITERE DE RECHERCHE : L'ATTRIBUT 'id'<br/>
* <br/>
* @param pId La valeur à rechercher pour l'attribut 'id' de l'entité.
* @return L'entité trouvée.
* @throws SpecieIdNotValidException L'attribut 'id' fourni est non valide.
* @throws SpecieNotFoundException L'entité recherchée est introuvable.
*/
@Override
public abstract Specie rechercherParId(Long pId) throws SpecieIdNotValidException, SpecieNotFoundException;
} |
(05.)Je créé une classe d'implémentation 'SpecieServiceImpl' :
Cette classe d'implémentation 'SpecieServiceImpl' réalisera les traitements métier relatifs à l'entité 'Specie'.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Service
public class SpecieServiceImpl implements SpecieService {
private SpecieRepository specieRepository;
/**
* <b>CONSTRUCTEUR</b><br/>
* ARGUMENTS : AUCUN<br/>
*/
public SpecieServiceImpl () {
...
}
@Autowired
public void setSpecieRepository (final SpecieRepository pSpecieRepository) {
...
}
@Override
public Specie rechercherParId(Long pId) throws SpecieIdNotValidException, SpecieNotFoundException {
...[code fourni au paragraphe (06.)]...
} |
(06.)Dans la classe 'SpecieServiceImpl': la méthode 'rechercherParId' et son implémentation
Cette méthode effectue les 2 tâches suivantes:
- Une recherche par id : en appelant la méthode 'findById' de l'objet 'specieRepository' (attribut de cette classe).
- La gestion des erreurs relatives à cette recherche : en traitant tous les cas d'erreur possibles.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Override
public Specie rechercherParId(Long pId) throws SpecieIdNotValidException, SpecieNotFoundException {
Optional<Specie> specieOptional = null;
try {
specieOptional = specieRepository.findById(pId);
} catch (IllegalArgumentException e) {
throw new SpecieIdNotValidException(FUNCTIONALITY__FIND_BY_ID + " -- " + ERROR__PROVIDED_ID__NOT_VALID);
}
if (!specieOptional.isPresent()) {
throw new SpecieNotFoundException(FUNCTIONALITY__FIND_BY_ID + " -- " + ERROR__SPECIE_TO_SEARCH__NOT_FOUND);
}
Specie specieTrouvee = specieOptional.get();
return specieTrouvee;
} |
(07.)Je créé une classe de test 'SpecieServiceTest' :
Cette classe de test réalisera les tests unitaires sur la classe métier 'SpecieServiceImpl'.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @RunWith(MockitoJUnitRunner.class)
public class SpecieServiceTest {
private static final Logger LOGGER = LoggerFactory.getLogger(SpecieServiceTest.class);
@Mock
private SpecieRepository specieRepositoryMock;
@InjectMocks
SpecieService specieService = new SpecieServiceImpl();
private List<Specie> species = new ArrayList<Specie>();
private int speciesSize;
/**
* <b>CONSTRUCTEUR</b><br/>
* ARGUMENTS : AUCUN<br/>
*/
public SpecieServiceTest () {}
/**
* <b>TACHES EFFECTUEES :</b><br/>
* ->ALIMENTER LA LISTE LOCALE DES ESPECES.<br/>
*/
@Before
public void setUp() {
...
}
/**
* <b>TEST SUR LA FONCTIONNALITE SUIVANTE :</b><br/>
* ->RECHERCHER UNE ESPECE.<br/>
* ->CRITERE DE RECHERCHE : L'ATTRIBUT 'ID'.<br/>
* <b>CAS DE TEST SUIVANT :</b><br/>
* ->L'ID FOURNI EST NON VALIDE.<br/>
*/
@Test
public void testRechercherById_DataNonValid() {
...[code fourni au paragraphe (08.)]...
}
} |
(08.)Dans la classe 'SpecieServiceTest': la méthode 'testRechercherById_DataNonValid' et son implémentation
Cette méthode effectue le test décrit ci-dessous :
- Une recherche par id : En appelant la méthode 'rechercherParId' de l'objet 'specieService' (attribut de cette classe).
- Le cas d'erreur provoqué : L'argument 'pId' passé à la méthode 'rechercherParId' est à 'null'.
- La gestion des erreurs : En traitant de manière différenciée le cas d'erreur attendu (l'argument 'id' non valide) des cas d'erreur non attendus.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test
public void testRechercherParId_DataNonValid() {
Mockito.when(this.specieRepositoryMock.findById(0L)).thenReturn(Optional.of(this.findSpecieById(0L)));
try {
this.specieService.rechercherParId(null);
Assert.fail("L'exception attendue n'a pas été lancée.");
} catch (SpecieIdNotValidException e) {
assertTrue("L'exception attendue a été lancée.", true);
} catch (Throwable t) {
Assert.fail("Une exception non attendue a été lancée.");
}
} |
(09.)L'exécution du test : Le déroulement observé et le déroulement attendu
(09.01.)Le comportement observé est décrit ci-dessous :
- (09.01.A.)Le composant 'SpecieServiceTest', dans sa méthode 'testRechercherParId_DataNonValid' :
Il appelle, dans le service 'SpecieServiceImpl', la méthode 'rechercherParId', avec un argument égal à 'null'.
- (09.01.B.)Le service 'SpecieServiceImpl', dans sa méthode 'rechercherParId' :
Il appelle, dans le dao 'SpecieRepository', la méthode 'findById', avec cet argument 'pId' égal à 'null'.
- (09.01.C.)Aucune exception n'est lancée par la méthode 'findById'.
La méthode 'findById' s'exécute sans erreur, et renvoie un objet de type Optional<Specie>, qui est non 'null' mais vide.
(09.02.)Le comportement attendu est décrit ci-dessous :
A l'étape (09.01.C.) : La méthode 'findById' aurait dû lancer une 'IllegalArgumentException'.
En effet, la javadoc confirme cela :
Citation:
- Dans la classe 'org.springframework.data.repository.CrudRepository'.
- Pour la méthode 'findById'.
- Si l'argument passé à cette méthode est égal à 'null', alors une 'IllegalArgumentException' est lancée.
Quelqu'un saurait-il expliquer pourquoi cette 'IllegalArgumentException' n'est pas lancée, SVP ?
La javadoc est-elle incorrecte ?
Toutes vos contributions sont la bienvenue.
Merci d'avance.
chat_roux