परिचय: वो एरर जो डेवलपर्स का सिरदर्द बन जाती है
\n\nक्या आपने कभी Android के लिए Kotlin में टेस्ट लिखा, ActivityTestRule जोड़ा, रन किया — और अचानक यह डरावना मैसेज देखा: "The @Rule must be public"? अगर हाँ — तो आप अकेले नहीं हैं। Stack Overflow पर इस सवाल के हज़ारों व्यूज़ हैं, और हर दिन दर्जनों डेवलपर्स इसी समस्या में फंसते हैं।
समस्या साधारण लगती है, लेकिन Kotlin में यह एक असली चुनौती बन जाती है। Java में सब कुछ आसानी से काम करता है: @Rule, पब्लिक फील्ड — और बस। लेकिन Kotlin में… आप जानते हैं। आज हम समझेंगे कि ऐसा क्यों होता है, और सबसे ज़रूरी, इसे हमेशा के लिए कैसे ठीक करें।
वैसे, अगर आप Python पसंद करते हैं, तो PythonLib पर टेस्टिंग से जुड़ी बहुत सी उपयोगी चीज़ें हैं। लेकिन आज — सिर्फ Kotlin और Android।
\n\n"The @Rule must be public" एरर क्यों आती है?
\n\nयाद कीजिए कि JUnit में ActivityTestRule कैसे काम करता है। यह एक Rule है जो हर टेस्ट से पहले Activity को लॉन्च करता है और बाद में सही तरीके से बंद करता है। Java में आप लिखते हैं:
@Rule\npublic ActivityTestRule<MainActivity> activityTestRule =\n new ActivityTestRule<>(MainActivity.class);\n\n\nसब आसान है: फील्ड पब्लिक है, Rule पब्लिक है। Kotlin में डिफ़ॉल्ट रूप से सब कुछ public होता है, लेकिन यहाँ एक पेंच है। जब आप लिखते हैं:
\n\n@Rule\nval activityTestRule = ActivityTestRule(MainActivity::class.java)\n\n\n…Kotlin एक प्राइवेट फील्ड और एक पब्लिक गेटर जनरेट करता है। जबकि JUnit को पब्लिक फील्ड चाहिए, गेटर नहीं। यही जादू है।
\n\nएरर "The @Rule must be public" इसलिए आती है क्योंकि JUnit आपका फील्ड नहीं देख पाता — वह प्राइवेट है।
समाधान #1: @get:Rule का उपयोग करें (सबसे लोकप्रिय)
\n\nसबसे आसान और आम तरीका है — एनोटेशन को फील्ड पर नहीं, बल्कि गेटर पर लगाना। इसके लिए Kotlin में @get:Rule सिंटैक्स है:
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यहाँ क्या हो रहा है? हम कह रहे हैं: "गेटर को एनोटेट करो, फील्ड को नहीं"। JUnit पब्लिक गेटर देखता है, Rule तक पहुँच जाता है, और सब काम करता है। सैकड़ों डेवलपर्स ने इसे आज़माया है।
\n\nयह समाधान आपका "प्लान A" है। यह छोटा, स्पष्ट है और किसी अतिरिक्त लाइब्रेरी की ज़रूरत नहीं है।
\n\nसमाधान #2: @JvmField से फील्ड को पब्लिक बनाएं
\n\nअगर आपको @get:Rule सिंटैक्स पसंद नहीं है (हालाँकि यह अच्छा है), तो @JvmField एनोटेशन का उपयोग कर सकते हैं। यह Kotlin को बिना गेटर के पब्लिक फील्ड जनरेट करने के लिए मजबूर करता है:
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यह बिल्कुल Java कोड की तरह काम करता है। नुकसान — थोड़ा ज़्यादा कोड। फायदा — साफ़ तौर पर बताता है कि फील्ड पब्लिक होना चाहिए।
\n\nमैं व्यक्तिगत रूप से @get:Rule पसंद करता हूँ — यह छोटा है और Kotlin डेवलपर के लिए ज़्यादा स्वाभाविक लगता है। लेकिन चुनाव आपका है।
समाधान #3: ActivityScenarioRule का उपयोग करें (आधुनिक तरीका)
\n\nअब बात करते हैं कि Google ActivityTestRule की जगह क्या सुझाता है। androidx.test.ext:junit लाइब्रेरी में ActivityScenarioRule है। यह ज़्यादा लचीला है और आधुनिक Android टेस्टिंग के साथ बेहतर काम करता है।
इसके साथ भी वही बात — @get:Rule या @JvmField का उपयोग करना होगा:
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