@Rule must be public em Kotlin: resolvendo o problema com ActivityTestRule

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Aquele erro que enlouquece

\n\n

Você já escreveu um teste em Kotlin para Android, adicionou ActivityTestRule, executou — e de repente viu aquela mensagem gelada: "The @Rule must be public"? Se sim — você não está sozinho. Esta pergunta no Stack Overflow acumulou milhares de visualizações, e todos os dias dezenas de desenvolvedores pisam no mesmo rastelo.

\n\n

O problema parece simples, mas em Kotlin ele se transforma em uma verdadeira aventura. Em Java tudo funciona perfeitamente: @Rule, campo público — e pronto. Já em Kotlin… bem, você sabe. Hoje vamos entender por que isso acontece e, principalmente, como corrigir isso de uma vez por todas.

\n\n

A propósito, se você prefere Python, no PythonLib também tem muito conteúdo útil sobre testes. Mas hoje — é Kotlin puro e Android.

\n\n

Por que ocorre o erro "The @Rule must be public"?

\n\n

Vamos lembrar como funciona o ActivityTestRule no JUnit. Esta é uma regra (Rule) que inicia a Activity antes de cada teste e a encerra corretamente após. Em Java você escreve:

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

Tudo simples: campo público, regra — public. Já em Kotlin, por padrão, tudo é public, mas aqui está o truque. Quando você escreve:

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

…o Kotlin gera um campo privado e um getter público. Mas o JUnit exige exatamente um campo público, e não um getter. Essa é toda a mágica.

\n\n

O erro "The @Rule must be public" aparece justamente porque o JUnit não enxerga seu campo — ele é privado.

\n\n

Solução nº 1: Usar @get:Rule (a mais popular)

\n\n

A maneira mais simples e comum é aplicar a anotação não ao campo, mas ao getter. Para isso, em Kotlin existe a sintaxe @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

O que acontece aqui? Dizemos: "Anote o getter, não o campo". O JUnit vê o getter público, obtém acesso à regra, e tudo funciona. Testado por centenas de desenvolvedores.

\n\n

Esta solução é seu "plano A". É concisa, clara e não requer bibliotecas adicionais.

\n\n

Solução nº 2: Tornar o campo público com @JvmField

\n\n

Se você não gosta da sintaxe @get:Rule (embora não devesse), pode usar a anotação @JvmField. Ela força o Kotlin a gerar um campo público sem 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

Funciona exatamente como o código Java. Desvantagem — um pouco mais de código. Vantagem — você indica explicitamente que o campo deve ser público.

\n\n

Pessoalmente, prefiro @get:Rule — é mais curto e parece mais natural para um desenvolvedor Kotlin. Mas a escolha é sua.

\n\n

Solução nº 3: Usar ActivityScenarioRule (forma moderna)

\n\n

Agora vamos falar sobre o que o Google recomenda em vez de ActivityTestRule. Na biblioteca androidx.test.ext:junit existe o ActivityScenarioRule. Ele é mais flexível e se integra melhor com os testes Android modernos.

\n\n

Com ele, a mesma história — é necessário usar @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