4.5 KiB
4.5 KiB
Stratégie de test — iMedia TV+
Principes
- TDD partout où c'est possible : écrire le test avant l'implémentation
- Couverture cible : > 70% sur les couches
data/etutil/ - Tests UI : parcours critiques uniquement (login, lecture, favoris)
- Exécution locale : tous les tests doivent passer avant chaque commit
Stack de test
| Technologie | Usage |
|---|---|
| JUnit 5 | Runner principal |
| MockK | Mocking Kotlin (classes, objects, extensions) |
| Turbine | Test des Flow (StateFlow, SharedFlow) |
| Kotlinx Coroutines Test | runTest, TestDispatcher |
| Compose UI Test | Test des écrans Compose TV |
| Espresso | Complément pour interactions système |
Tests unitaires
data/repository/
| Fichier | Scénarios |
|---|---|
AuthRepositoryTest.kt |
✅ Réponse valide → auth == "1" → Success |
✅ Réponse valide → auth == "0" → Error("Identifiants incorrects") |
|
✅ Timeout → Error("Le serveur ne répond pas") |
|
✅ Socket error → Error("Aucune connexion réseau") |
|
LiveRepositoryTest.kt |
✅ Catégories chargées → liste triée FR/AR/autres |
| ✅ Chaînes filtrées par catégorie | |
| ✅ Flux vide → liste vide (pas de crash) | |
EpgRepositoryTest.kt |
✅ Cache valide (< 2min) → pas d'appel API |
| ✅ Cache expiré → appel API + mise à jour cache | |
| ✅ Pas de cache → appel API + insertion | |
✅ EPG vide → Error("Aucun programme") |
|
FavoritesRepositoryTest.kt |
✅ Ajout favori → liste contient l'ID |
| ✅ Suppression → liste ne contient plus l'ID | |
| ✅ Persistance → données conservées après recreation du repo | |
SeriesRepositoryTest.kt |
✅ Détail série retourne saisons + épisodes |
| ✅ Épisode sans titre → fallback "Épisode N" |
data/storage/
| Fichier | Scénarios |
|---|---|
SecureStorageTest.kt |
✅ Valeur écrite = valeur lue |
| ✅ Données différentes après clair vs chiffré | |
| ✅ Clear supprime toutes les données | |
| ✅ Valeur inexistante → null safe |
util/
| Fichier | Scénarios |
|---|---|
CategorySortUtilsTest.kt |
✅ Catégories FR en premier |
| ✅ Catégories AR ensuite | |
| ✅ Catégories "Toutes" et "Favoris" toujours en haut | |
| ✅ Autres en dernier | |
| ✅ Ordre alphabétique à l'intérieur des groupes | |
XtreamUrlBuilderTest.kt |
✅ URL Live correcte avec m3u8 |
| ✅ URL VOD avec extension dynamique | |
| ✅ URL série avec épisode_id | |
| ✅ URL Replay avec timestamp formaté |
Tests d'intégration / UI
| Fichier | Parcours |
|---|---|
LoginScreenTest.kt |
✅ Remplit les champs → bouton Connexion activé |
| ✅ Champs vides → bouton désactivé | |
| ✅ Erreur réseau → message d'erreur visible | |
| ✅ Connexion OK → navigation vers HomeScreen | |
HomeScreenTest.kt |
✅ Catégories affichées au chargement |
| ✅ Changement de catégorie → nouvelles chaînes | |
| ✅ Favori accessible avec télécommande (DPAD) | |
| ✅ Recherche par nom fonctionne | |
PlayerScreenTest.kt |
✅ Overlay visible → disparaît après 3s |
| ✅ Zapping gauche → chaîne précédente | |
| ✅ Premier retour → overlay réapparaît | |
| ✅ Deuxième retour → retour à HomeScreen |
Exécution
# Tout
.\gradlew.bat test connectedAndroidTest --no-daemon
# Tests unitaires uniquement
.\gradlew.bat test --no-daemon
# Tests UI
.\gradlew.bat connectedAndroidTest --no-daemon
# Filtre par classe
.\gradlew.bat test --no-daemon --tests *AuthRepositoryTest*
.\gradlew.bat connectedAndroidTest --no-daemon --tests *LoginScreenTest*
# Rapport de couverture
.\gradlew.bat :app:createDebugCoverageReport --no-daemon
# HTML → app/build/reports/coverage/debug/index.html
Convention d'écriture
class AuthRepositoryTest {
private val api = mockk<XtreamApi>()
private val storage = mockk<SecureStorage>()
private val repo = AuthRepository(api, storage)
@Test
fun `login with valid credentials returns Success`() = runTest {
// Given
coEvery { api.authenticate(any(), any(), any()) } returns AuthResponse(
userInfo = UserInfo(username = "test"),
auth = "1",
status = "Active"
)
// When
val result = repo.login("http://server", "user", "pass")
// Then
assertTrue(result is UiState.Success)
assertEquals("test", (result as UiState.Success).data.username)
}
}