La fiabilité de la logique métier est souvent mise à mal par des changements dans l’application.
Cela peut entraîner des erreurs coûteuses et un manque de confiance des utilisateurs envers le logiciel.
En intégrant des tests unitaires pour la couche domaine, l’article explore comment assurer une logique métier stable et fiable.
Développez des applications Android solides, fiables et efficaces avec une approche TDD
Dans cette section, nous introduirons ce que sont les tests unitaires et pourquoi ils sont essentiels dans le développement logiciel, en particulier pour tester la couche domaine.
Un test unitaire est une méthode pour tester des unités de code individuelles, telles que des fonctions ou des classes, afin de vérifier leur bon fonctionnement. Ces tests permettent de s’assurer que chaque partie du code fonctionne de manière isolée avant d’être intégrée dans le système complet.
La couche domaine est indépendante du reste de l’application. Elle représente les règles métier et les objets du domaine (modèles), sur lesquels d’autres parties de l’application se basent. Tester cette couche permet de garantir que la logique métier reste correct, même si d’autres parties de l’application changent.
Domaines et Use Cases en Tests Unitaires
Les Objets du Domaine
Les objets du domaine sont généralement simples. Ils représentent des entités de votre système, comme Utilisateur, avec des propriétés et méthodes qui respectent la logique métier. Dans le cadre de notre exemple, un objet Utilisateur pourrait ressembler à ceci :
Ce code en Kotlin définit deux propriétés immuables (variables en lecture seule) au sein d’une classe ou d’une fonction. La propriété id est de type entier ( Int ), tandis que nom est de type chaîne de caractères ( String ). Ces deux propriétés sont utilisées pour stocker un identifiant et un nom, respectivement.
data class Utilisateur(val id : Int, val nom : String)
Les Use Cases
Ce code en Kotlin définit une classe GetUtilisateurUseCase , qui représente un use case, c’est-à-dire un scénario d’utilisation dans l’application, visant à récupérer les informations d’un utilisateur. Cette classe dépend d’un objet UtilisateurRepository, passé comme paramètre via son constructeur, qui est responsable de l’accès aux données des utilisateurs. La méthode execute(id: Int) prend un identifiant d’utilisateur en entrée et utilise la méthode getUtilisateurById(id) du repository pour retourner un objet de type Utilisateur correspondant à cet identifiant. Ce code respecte le principe de séparation des préoccupations en encapsulant la logique métier dans le use case et en déléguant l’accès aux données à un repository.
class GetUtilisateurUseCase(
private val utilisateurRepository : UtilisateurRepository) {
fun execute(id : Int) : Utilisateur {
return utilisateurRepository.getUtilisateurById(id)
}
}
Cette figure présente la structure de l’architecture d’un projet Android en suivant une organisation en couches. À partir de ce projet, on observe la séparation claire entre les différentes responsabilités : la couche domain contient notamment les dossiers pour les use cases , comme GetUtilisateurUseCase ,
Dans cet exemple, GetUtilisateurUseCase dépend du UtilisateurRepository, une interface pour l’accès aux données.
Tests Unitaires des Use Cases en Kotlin
Création d’un Test Unitaire
Commençons par un test unitaire pour notre use case. Pour ce faire, nous devons créer un test qui simule l’exécution du use case GetUtilisateurUseCase et vérifier que l’utilisateur renvoyé est bien celui attendu.
Voici un exemple de test unitaire :
Ce code est un test unitaire pour la classe GetUtilisateurUseCase en utilisant le framework Mockito. La classe est annotée avec @RunWith(MockitoJUnitRunner::class) pour activer les fonctionnalités de Mockito, et un mock de UtilisateurRepository est créé avec @Mock . Avant chaque test, la méthode setUp() initialise une instance de GetUtilisateurUseCase avec le mock du repository. Le test should return utilisateur when valid id is provided vérifie que lorsqu’un ID valide est fourni, la méthode execute(id) retourne l’utilisateur attendu. Mockito est utilisé pour simuler le comportement du repository, et la méthode assertEquals vérifie que le résultat est bien celui attendu.
@RunWith(MockitoJUnitRunner::class) class GetUtilisateurUseCaseTest {
@Mock lateinit var utilisateurRepository
: UtilisateurRepository lateinit var getUtilisateurUseCase
: GetUtilisateurUseCase @Before fun
setUp() {
getUtilisateurUseCase = GetUtilisateurUseCase(utilisateurRepository)
}
@Test fun `should return utilisateur when valid id is provided`() {
// Given
val id = 1 val utilisateurAttendu =
Utilisateur(id, "John Doe")
Mockito.`when`(utilisateurRepository.getUtilisateurById(id))
.thenReturn(utilisateurAttendu)
// When
val resultat = getUtilisateurUseCase.execute(id)
// Then
assertEquals(utilisateurAttendu, resultat)
}
}
Dans cet exemple :
- Nous utilisons Mockito pour moquer le UtilisateurRepository.
- Nous définissons un utilisateur attendu et simulons la réponse du repository.
Élément | Description |
---|---|
Classe de Test | GetUtilisateurUseCaseTest teste le cas d’utilisation. |
Mock | utilisateurRepository est un mock de UtilisateurRepository. |
Initialisation | getUtilisateurUseCase est initialisé dans setUp(). |
Test Principal | Vérifie que l’utilisateur est retourné pour un ID valide. |
Préparation des Données | ID et utilisateur attendu sont définis pour le test. |
Mise en place du Mock | Définit le comportement du mock avec Mockito. |
Assertion | Vérifie que le résultat correspond à l’utilisateur attendu. |
Structurer la Logique Métier en Tests
Il est important de structurer les tests de manière cohérente avec l’architecture du projet. Par exemple, il est recommandé de reproduire la hiérarchie des packages dans les dossiers de test. Si votre classe GetUtilisateurUseCase se trouve dans le package usecase, votre classe de test devrait également se trouver dans le même package sous le dossier de test.
Organisation des Tests
- Dossier source :src/main/kotlin
- Dossier de test :src/test/kotlin
Cette figure présente une organisation claire des dossiers de code source et de tests dans un projet Kotlin. Le dossier principal, src , contient deux sous-dossiers distincts : main/kotlin pour le code source de l’application et test/kotlin pour les tests unitaires. Le diagramme montre que la classe GetUtilisateurUseCase est située dans le dossier usecase sous main/kotlin , tandis que sa classe de test associée, GetUtilisateurUseCaseTest , se trouve dans le même package usecase , mais sous test/kotlin . Cette structure assure une correspondance cohérente entre le code source et les tests, facilitant la gestion et la maintenabilité du projet.
En suivant cette convention, la classe de test pour GetUtilisateurUseCase serait située dans src/test/kotlin/usecase.
Mockito : Moquer les Dépendances Efficacement
Lors des tests, il est souvent nécessaire de moquer les objets dont dépend le code testé. Ici, nous utilisons Mockito pour moquer le UtilisateurRepository qui est passé en paramètre au use case.
Exemple de Moquerie avec Mockito
Pour moquer une dépendance, nous utilisons l’annotation @Mock et configurons son comportement avec Mockito.when()`. Voici un exemple :
Ce code Kotlin utilise Mockito pour effectuer un test unitaire de la classe GetUtilisateurUseCase . Il commence par définir un mock pour UtilisateurRepository , qui simule le comportement du dépôt de données sans avoir besoin d’une véritable base de données. Dans la méthode setUp() , une instance de GetUtilisateurUseCase est initialisée avec ce mock. Ensuite, le test principal vérifie que lorsque l’on passe un identifiant d’utilisateur valide, la méthode execute() renvoie correctement un objet Utilisateur . Le comportement du mock est configuré pour retourner un utilisateur spécifique, et la méthode assertEquals() est utilisée pour valider que le résultat correspond à l’utilisateur attendu. Ce test garantit que la logique métier fonctionne correctement en isolation.
@Mock lateinit var utilisateurRepository : UtilisateurRepository @Before fun
setUp() {
getUtilisateurUseCase = GetUtilisateurUseCase(utilisateurRepository)
}
@Test fun `should return utilisateur when valid id is provided`() {
val id = 1 val utilisateurAttendu =
Utilisateur(id, "John Doe")
Mockito.`when`(utilisateurRepository.getUtilisateurById(id))
.thenReturn(utilisateurAttendu) val resultat =
getUtilisateurUseCase.execute(id)
assertEquals(utilisateurAttendu, resultat)
}
Cette figure présente un extrait d’un projet Android qui montre l’organisation du code source ainsi qu’un exemple de test unitaire pour la classe GetUtilisateurUseCase . À gauche, on peut voir la structure des fichiers du projet, où la classe de test GetUtilisateurUseCaseTest est placée dans le même package que la classe GetUtilisateurUseCase , sous le répertoire test . À droite, le test utilise le framework JUnit avec Mockito pour vérifier la méthode execute() de la classe GetUtilisateurUseCase .
Ce type de moquerie permet d’isoler le code testé des autres composants du système, comme la base de données ou les appels réseau.
Formez-vous gratuitement avec Alphorm !
Maîtrisez les compétences clés en IT grâce à nos formations gratuites et accélérez votre carrière dès aujourd'hui.
FAQ
Qu'est-ce qu'un test unitaire ?
Pourquoi tester la couche domaine ?
Comment utiliser Mockito pour les tests unitaires ?
Comment structurer les tests unitaires dans un projet Kotlin ?
Quels sont les avantages d'utiliser des use cases ?
Conclusion
Les tests unitaires sont un outil puissant pour garantir la fiabilité de votre logiciel. Comment pouvez-vous adapter ces pratiques à d’autres aspects de votre projet ?