@Rule muss public sein in Kotlin: Lösung des Problems mit ActivityTestRule

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Der Fehler, der einen in den Wahnsinn treibt

\n\n

Haben Sie jemals einen Kotlin-Test für Android geschrieben, ActivityTestRule hinzugefügt, gestartet – und plötzlich diese erschreckende Meldung gesehen: "The @Rule must be public"? Wenn ja – Sie sind nicht allein. Diese Frage auf Stack Overflow hat tausende Aufrufe gesammelt, und jeden Tag tappen Dutzende Entwickler in dieselbe Falle.

\n\n

Das Problem scheint einfach, aber in Kotlin wird es zu einer echten Herausforderung. In Java funktioniert alles reibungslos: @Rule, öffentliches Feld – und fertig. In Kotlin jedoch ... nun, Sie wissen schon. Heute werden wir verstehen, warum das passiert, und vor allem, wie man es ein für alle Mal behebt.

\n\n

Übrigens, wenn Sie Python bevorzugen, gibt es auf PythonLib auch viel Nützliches zum Thema Testing. Aber heute – reines Kotlin und Android.

\n\n

Warum tritt der Fehler "The @Rule must be public" auf?

\n\n

Erinnern wir uns, wie ActivityTestRule in JUnit funktioniert. Es ist eine Regel (Rule), die vor jedem Test eine Activity startet und sie danach korrekt beendet. In Java schreiben Sie:

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

Ganz einfach: Das Feld ist öffentlich, die Regel ist public. In Kotlin ist standardmäßig alles public, aber hier gibt es einen Haken. Wenn Sie schreiben:

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

…generiert Kotlin ein privates Feld und einen öffentlichen Getter. JUnit benötigt jedoch ein öffentliches Feld, keinen Getter. Das ist die ganze Magie.

\n\n

Der Fehler "The @Rule must be public" tritt genau deshalb auf, weil JUnit Ihr Feld nicht sieht – es ist privat.

\n\n

Lösung Nr. 1: Verwenden von @get:Rule (die beliebteste)

\n\n

Der einfachste und gebräuchlichste Weg ist, die Annotation nicht auf das Feld, sondern auf den Getter anzuwenden. Dafür gibt es in Kotlin die Syntax @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

Was passiert hier? Wir sagen: "Annotiere den Getter, nicht das Feld". JUnit sieht den öffentlichen Getter, erhält Zugriff auf die Regel, und alles funktioniert. Getestet von Hunderten von Entwicklern.

\n\n

Diese Lösung ist Ihr "Plan A". Sie ist prägnant, verständlich und erfordert keine zusätzlichen Bibliotheken.

\n\n

Lösung Nr. 2: Das Feld mit @JvmField öffentlich machen

\n\n

Wenn Sie die Syntax @get:Rule nicht mögen (obwohl zu Unrecht), können Sie die Annotation @JvmField verwenden. Sie zwingt Kotlin, ein öffentliches Feld ohne Getter zu generieren:

\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

Funktioniert genauso wie der Java-Code. Nachteil – etwas mehr Code. Vorteil – Sie geben explizit an, dass das Feld öffentlich sein soll.

\n\n

Persönlich bevorzuge ich @get:Rule – es ist kürzer und liest sich für einen Kotlin-Entwickler natürlicher. Aber die Wahl liegt bei Ihnen.

\n\n

Lösung Nr. 3: Verwenden von ActivityScenarioRule (moderner Weg)

\n\n

Und jetzt sprechen wir darüber, was Google anstelle von ActivityTestRule empfiehlt. In der Bibliothek androidx.test.ext:junit gibt es ActivityScenarioRule. Es ist flexibler und lässt sich besser in modernes Android-Testing integrieren.

\n\n

Damit ist es die gleiche Geschichte – Sie müssen @get:Rule oder @JvmField verwenden:

\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

Buchempfehlungen