@Rule doit être public en Kotlin : résolution du problème avec ActivityTestRule

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Cette fameuse erreur qui rend fou

\n\n

Avez-vous déjà écrit un test en Kotlin pour Android, ajouté ActivityTestRule, lancé — et soudainement vu ce message glaçant : "The @Rule must be public" ? Si oui, vous n'êtes pas seul. Cette question sur Stack Overflow a recueilli des milliers de vues, et chaque jour, des dizaines de développeurs tombent dans le même piège.

\n\n

Le problème semble simple, mais en Kotlin, il se transforme en véritable quête. En Java, tout fonctionne comme sur des roulettes : @Rule, un champ public — et le tour est joué. Mais en Kotlin… eh bien, vous savez. Aujourd'hui, nous allons comprendre pourquoi cela se produit et, surtout, comment le corriger une fois pour toutes.

\n\n

Au fait, si vous préférez Python, PythonLib propose aussi beaucoup de contenu utile sur les tests. Mais aujourd'hui, c'est du Kotlin et de l'Android purs.

\n\n

Pourquoi l'erreur "The @Rule must be public" apparaît-elle ?

\n\n

Rappelons comment fonctionne ActivityTestRule dans JUnit. C'est une règle (Rule) qui lance une Activity avant chaque test et la termine correctement après. En Java, vous écrivez :

\n\n
@Rule\npublic ActivityTestRule<MainActivity> activityTestRule =\n    new ActivityTestRule<>(MainActivity.class);\n
\n\n

Tout est simple : le champ est public, la règle est public. En Kotlin, tout est public par défaut, mais il y a un piège. Lorsque vous écrivez :

\n\n
@Rule\nval activityTestRule = ActivityTestRule(MainActivity::class.java)\n
\n\n

…Kotlin génère un champ privé et un getter public. Or, JUnit exige un champ public, pas un getter. C'est là toute la magie.

\n\n

L'erreur "The @Rule must be public" apparaît précisément parce que JUnit ne voit pas votre champ — il est privé.

\n\n

Solution n°1 : Utiliser @get:Rule (la plus populaire)

\n\n

La méthode la plus simple et la plus courante consiste à appliquer l'annotation non pas au champ, mais au getter. Pour cela, Kotlin propose la syntaxe @get:Rule :

\n\n
class MainActivityTest {\n\n    @get:Rule\n    val activityTestRule = ActivityTestRule(MainActivity::class.java)\n\n    @Test\n    fun testActivityIsNotNull() {\n        assertNotNull(activityTestRule.activity)\n    }\n}\n
\n\n

Que se passe-t-il ici ? Nous disons : "Annote le getter, pas le champ". JUnit voit le getter public, accède à la règle, et tout fonctionne. Testé par des centaines de développeurs.

\n\n

Cette solution est votre "plan A". Elle est concise, claire et ne nécessite pas de bibliothèques supplémentaires.

\n\n

Solution n°2 : Rendre le champ public avec @JvmField

\n\n

Si vous n'aimez pas la syntaxe @get:Rule (à tort), vous pouvez utiliser l'annotation @JvmField. Elle force Kotlin à générer un champ public sans getter :

\n\n
class MainActivityTest {\n\n    @JvmField\n    @Rule\n    val activityTestRule = ActivityTestRule(MainActivity::class.java)\n\n    @Test\n    fun testActivityIsNotNull() {\n        assertNotNull(activityTestRule.activity)\n    }\n}\n
\n\n

Cela fonctionne exactement comme le code Java. Inconvénient : un peu plus de code. Avantage : vous indiquez explicitement que le champ doit être public.

\n\n

Personnellement, je préfère @get:Rule — c'est plus court et plus naturel pour un développeur Kotlin. Mais le choix vous appartient.

\n\n

Solution n°3 : Utiliser ActivityScenarioRule (la méthode moderne)

\n\n

Parlons maintenant de ce que Google recommande à la place d'ActivityTestRule. Dans la bibliothèque androidx.test.ext:junit, il y a ActivityScenarioRule. Il est plus flexible et s'intègre mieux avec les tests Android modernes.

\n\n

Avec lui, c'est la même histoire — il faut utiliser @get:Rule ou @JvmField :

\n\n
class MainActivityTest {\n\n    @get:Rule\n    val activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)\n\n    @Test\n    fun testActivityIsNotNull() {\n        activityScenarioRule.scenario.onActivity { activity ->\n                

Blogs

Book Recommendations