हिल्ट टेस्टिंग गाइड

Hilt जैसे डिपेंडेंसी इंजेक्शन फ़्रेमवर्क का इस्तेमाल करने का एक फ़ायदा यह है कि इससे आपके कोड की जांच करना आसान हो जाता है.

यूनिट टेस्ट

यूनिट टेस्ट के लिए Hilt का इस्तेमाल करना ज़रूरी नहीं है. ऐसा इसलिए, क्योंकि कंस्ट्रक्टर इंजेक्शन का इस्तेमाल करने वाली क्लास को टेस्ट करते समय, आपको उस क्लास को इंस्टैंटिएट करने के लिए Hilt का इस्तेमाल करने की ज़रूरत नहीं होती. इसके बजाय, फ़र्ज़ी या मॉक डिपेंडेंसी पास करके, सीधे तौर पर किसी क्लास कंस्ट्रक्टर को कॉल किया जा सकता है. ठीक उसी तरह जैसे कंस्ट्रक्टर को एनोटेट नहीं किया गया है:

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

class AnalyticsAdapterTest {

  @Test
  fun `Happy path`() {
    // You don't need Hilt to create an instance of AnalyticsAdapter.
    // You can pass a fake or mock AnalyticsService.
    val adapter = AnalyticsAdapter(fakeAnalyticsService)
    assertEquals(...)
  }
}

hiltViewModel() को कॉल करके, कंपोज़ेबल में हासिल की गई ViewModel क्लास पर भी यही नियम लागू होता है. यूनिट टेस्ट में, ViewModel को सीधे तौर पर फ़ेक के साथ बनाएं. ViewModel से कंपोज़ेबल में स्टेट कैसे फ़्लो होती है, इस बारे में जानकारी के लिए स्टेट और Jetpack Compose और स्टेट को कहां होस्ट करें लेख पढ़ें.

शुरू से आखिर तक के टेस्ट

इंटिग्रेशन टेस्ट के लिए, Hilt डिपेंडेंसी को उसी तरह इंजेक्ट करता है जिस तरह वह आपके प्रोडक्शन कोड में करता है. Hilt के साथ टेस्टिंग करने के लिए, किसी रखरखाव की ज़रूरत नहीं होती. ऐसा इसलिए, क्योंकि Hilt हर टेस्ट के लिए कॉम्पोनेंट का नया सेट अपने-आप जनरेट करता है.

टेस्टिंग डिपेंडेंसी जोड़ना

अपने टेस्ट में Hilt का इस्तेमाल करने के लिए, अपने प्रोजेक्ट में hilt-android-testing डिपेंडेंसी शामिल करें:

dependencies {
    // For Robolectric tests.
    testImplementation("com.google.dagger:hilt-android-testing:2.57.1")
    kspTest("com.google.dagger:hilt-android-compiler:2.57.1")

    // For instrumented tests.
    androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1")
    kspAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1")

    // Compose UI test rule.
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-test-manifest")

}

यूज़र इंटरफ़ेस (यूआई) की जांच का सेटअप

Hilt का इस्तेमाल करने वाले किसी भी यूज़र इंटरफ़ेस (यूआई) टेस्ट को @HiltAndroidTest से एनोटेट करना ज़रूरी है. इस एनोटेशन की मदद से, हर टेस्ट के लिए Hilt कॉम्पोनेंट जनरेट किए जाते हैं.

साथ ही, आपको टेस्ट क्लास में HiltAndroidRule जोड़ना होगा. यह कॉम्पोनेंट की स्थिति को मैनेज करता है. इसका इस्तेमाल आपके टेस्ट में इंजेक्शन करने के लिए किया जाता है:

@HiltAndroidTest
class SettingsScreenTest {

    @get:Rule(order = 0)
    val hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val composeRule = createAndroidComposeRule<HiltTestActivity>()

    // Compose UI tests here.
}

इसके बाद, आपके टेस्ट को Application क्लास के बारे में पता होना चाहिए. यह क्लास, Hilt आपके लिए अपने-आप जनरेट करता है.

Hilt को डिपेंडेंसी इंजेक्ट करने की अनुमति देने के लिए, आपको अपने androidTest सोर्स सेट में HiltTestActivity नाम की एक खाली गतिविधि बनानी होगी. साथ ही, इसे @AndroidEntryPoint के साथ एनोटेट करना होगा. इसके बाद, createAndroidComposeRule इस गतिविधि का इस्तेमाल, आपके कंपोज़ेबल कॉन्टेंट के होस्ट के तौर पर करता है.

टेस्ट ऐप्लिकेशन

आपको Hilt का इस्तेमाल करने वाले इंस्ट्रुमेंटेड टेस्ट को, Hilt के साथ काम करने वाले Application ऑब्जेक्ट में चलाना होगा. यह लाइब्रेरी, टेस्ट में इस्तेमाल करने के लिए HiltTestApplication उपलब्ध कराती है. अगर आपके टेस्ट के लिए किसी दूसरे ऐप्लिकेशन की ज़रूरत है, तो टेस्ट के लिए कस्टम ऐप्लिकेशन देखें.

आपको अपने टेस्ट ऐप्लिकेशन को इंस्ट्रुमेंटेड टेस्ट या Robolectric टेस्ट में चलाने के लिए सेट करना होगा. यहां दिए गए निर्देश, खास तौर पर Hilt के लिए नहीं हैं. हालांकि, ये टेस्ट में चलाने के लिए कस्टम ऐप्लिकेशन तय करने के बारे में सामान्य दिशा-निर्देश हैं.

इंस्ट्रुमेंट किए गए टेस्ट में टेस्ट ऐप्लिकेशन सेट करना

इंस्ट्रुमेंट किए गए टेस्ट में Hilt टेस्ट ऐप्लिकेशन का इस्तेमाल करने के लिए, आपको नया टेस्ट रनर कॉन्फ़िगर करना होगा. इससे, आपके प्रोजेक्ट में इंस्ट्रुमेंट किए गए सभी टेस्ट के लिए Hilt काम करता है. यह तरीका अपनाएं:

  1. androidTest फ़ोल्डर में AndroidJUnitRunner को बढ़ाने वाली कस्टम क्लास बनाएं.
  2. newApplication फ़ंक्शन को बदलें और जनरेट किए गए Hilt टेस्ट ऐप्लिकेशन का नाम पास करें.
// A custom runner to set up the instrumented application class for tests.
class CustomTestRunner : AndroidJUnitRunner() {

    override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
        return super.newApplication(cl, HiltTestApplication::class.java.name, context)
    }
}

इसके बाद, इस टेस्ट रनर को अपनी Gradle फ़ाइल में कॉन्फ़िगर करें. इसके लिए, इंस्ट्रुमेंटेड यूनिट टेस्ट गाइड में दिया गया तरीका अपनाएं. पक्का करें कि आपने पूरा क्लासपाथ इस्तेमाल किया हो:

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
Robolectric टेस्ट में टेस्ट ऐप्लिकेशन सेट करना

अगर यूज़र इंटरफ़ेस (यूआई) लेयर की जांच करने के लिए Robolectric का इस्तेमाल किया जाता है, तो robolectric.properties फ़ाइल में यह तय किया जा सकता है कि किस ऐप्लिकेशन का इस्तेमाल करना है:

application = dagger.hilt.android.testing.HiltTestApplication

इसके अलावा, Robolectric के @Config एनोटेशन का इस्तेमाल करके, हर टेस्ट के लिए ऐप्लिकेशन को अलग-अलग कॉन्फ़िगर किया जा सकता है:

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
class SettingsScreenTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}

टेस्टिंग की सुविधाएं

टेस्ट में Hilt का इस्तेमाल करने के लिए तैयार हो जाने के बाद, टेस्टिंग प्रोसेस को पसंद के मुताबिक बनाने के लिए कई सुविधाओं का इस्तेमाल किया जा सकता है.

जांच में टाइप इंजेक्ट करना

किसी टेस्ट में टाइप इंजेक्ट करने के लिए, फ़ील्ड इंजेक्शन के लिए @Inject का इस्तेमाल करें. Hilt को @Inject फ़ील्ड भरने के लिए कहने के लिए, hiltRule.inject() पर कॉल करें.

इंस्ट्रुमेंट किए गए टेस्ट का यह उदाहरण देखें:

@HiltAndroidTest
class SettingsScreenTest {

    @get:Rule(order = 0)
    val hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val composeRule = createAndroidComposeRule<HiltTestActivity>()

    @Inject
    lateinit var analyticsAdapter: AnalyticsAdapter

    @Before
    fun init() {
        hiltRule.inject()
    }

    @Test
    fun settingsScreen_showsTitle() {
        composeRule.setContent {
            SettingsScreen()
        }
        composeRule.onNodeWithText("Settings").assertIsDisplayed()
        // analyticsRepository is available here.
    }
}

बाइंडिंग बदलना

अगर आपको किसी डिपेंडेंसी का फ़र्ज़ी या मॉक इंस्टेंस इंजेक्ट करना है, तो आपको Hilt को यह बताना होगा कि वह प्रोडक्शन कोड में इस्तेमाल किए गए बाइंडिंग का इस्तेमाल न करे और इसके बजाय किसी दूसरे बाइंडिंग का इस्तेमाल करे. किसी बाइंडिंग को बदलने के लिए, आपको उस मॉड्यूल को बदलना होगा जिसमें बाइंडिंग शामिल है. इसके लिए, आपको एक ऐसा टेस्ट मॉड्यूल इस्तेमाल करना होगा जिसमें वे बाइंडिंग शामिल हों जिनका इस्तेमाल आपको टेस्ट में करना है.

उदाहरण के लिए, मान लें कि आपका प्रोडक्शन कोड, AnalyticsService के लिए बाइंडिंग इस तरह से दिखाता है:

@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

जांच में AnalyticsService बाइंडिंग को बदलने के लिए, test या androidTest फ़ोल्डर में फ़र्ज़ी डिपेंडेंसी वाला नया Hilt मॉड्यूल बनाएं. साथ ही, इसे @TestInstallIn के साथ एनोटेट करें. उस फ़ोल्डर में मौजूद सभी टेस्ट में, फ़र्ज़ी डिपेंडेंसी डाली जाती है.

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [AnalyticsModule::class]
)
abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    fakeAnalyticsService: FakeAnalyticsService
  ): AnalyticsService
}

आम तौर पर, कंपोज़ेबल इन डिपेंडेंसी का इस्तेमाल सीधे तौर पर नहीं करते. वे hiltViewModel() की मदद से हासिल किए गए ViewModel के ज़रिए इनका इस्तेमाल करते हैं. इसलिए, Hilt में बाइंडिंग को बदलने से काम हो जाएगा. टेस्ट किए जा रहे कंपोज़ेबल में, फ़र्ज़ी डेटा अपने-आप दिख जाता है.

किसी एक टेस्ट में बाइंडिंग बदलना

अगर आपको सभी टेस्ट के बजाय, सिर्फ़ एक टेस्ट में बाइंडिंग बदलनी है, तो @UninstallModules एनोटेशन का इस्तेमाल करके, टेस्ट से Hilt मॉड्यूल अनइंस्टॉल करें. इसके बाद, टेस्ट के अंदर एक नया टेस्ट मॉड्यूल बनाएं.

पिछले वर्शन के AnalyticsService उदाहरण के मुताबिक, टेस्ट क्लास में @UninstallModules एनोटेशन का इस्तेमाल करके, Hilt को प्रोडक्शन मॉड्यूल को अनदेखा करने के लिए कहें:

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsScreenTest { ... }

इसके बाद, आपको बाइंडिंग बदलनी होगी. टेस्ट क्लास में एक नया मॉड्यूल बनाएं. यह मॉड्यूल, टेस्ट बाइंडिंग को तय करता है:

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsScreenTest {

  @Module
  @InstallIn(SingletonComponent::class)
  abstract class TestModule {

    @Singleton
    @Binds
    abstract fun bindAnalyticsService(
      fakeAnalyticsService: FakeAnalyticsService
    ): AnalyticsService
  }

  // ...
}

इससे सिर्फ़ एक टेस्ट क्लास के लिए बाइंडिंग बदलती है. अगर आपको सभी टेस्ट क्लास के लिए बाइंडिंग बदलनी है, तो ऊपर दिए गए सेक्शन में मौजूद @TestInstallIn एनोटेशन का इस्तेमाल करें. इसके अलावा, Robolectric टेस्ट के लिए test मॉड्यूल में या इंस्ट्रुमेंटेड टेस्ट के लिए androidTest मॉड्यूल में टेस्ट बाइंडिंग को रखा जा सकता है. हमारा सुझाव है कि जब भी हो सके, @TestInstallIn का इस्तेमाल करें.

नई वैल्यू बाइंड करना

अपने टेस्ट के फ़ील्ड को Hilt के डिपेंडेंसी ग्राफ़ में आसानी से बाइंड करने के लिए, @BindValue एनोटेशन का इस्तेमाल करें. किसी फ़ील्ड को @BindValue के साथ एनोटेट करें. इसके बाद, उसे घोषित किए गए फ़ील्ड टाइप के तहत बाइंड कर दिया जाएगा. साथ ही, उस फ़ील्ड के लिए मौजूद किसी भी क्वालिफ़ायर को भी बाइंड कर दिया जाएगा.

AnalyticsService उदाहरण में, @BindValue का इस्तेमाल करके, AnalyticsService को a fake से बदला जा सकता है:

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsScreenTest {

  @BindValue @JvmField
  val analyticsService: AnalyticsService = FakeAnalyticsService()

  ...
}

इससे, टेस्ट में बाइंडिंग को बदलना और बाइंडिंग को रेफ़रंस करना आसान हो जाता है. ऐसा इसलिए, क्योंकि इन दोनों को एक साथ किया जा सकता है.

@BindValue, क्वालिफ़ायर और टेस्टिंग से जुड़ी अन्य एनोटेशन के साथ काम करता है. उदाहरण के लिए, अगर Mockito जैसी टेस्टिंग लाइब्रेरी का इस्तेमाल किया जाता है, तो उसे Robolectric टेस्ट में इस तरह इस्तेमाल किया जा सकता है:

...
class SettingsScreenTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

अगर आपको मल्टीबाइंडिंग जोड़नी है, तो @BindValue की जगह @BindValueIntoSet और @BindValueIntoMap एनोटेशन का इस्तेमाल किया जा सकता है. @BindValueIntoMap के लिए, आपको फ़ील्ड को मैप कुंजी के एनोटेशन के साथ एनोटेट करना होगा.

विशेष मामले

Hilt, इस्तेमाल के गैर-स्टैंडर्ड उदाहरणों के लिए भी सुविधाएं उपलब्ध कराता है.

टेस्ट के लिए कस्टम ऐप्लिकेशन

अगर आपको HiltTestApplication का इस्तेमाल करने में समस्या आ रही है, क्योंकि आपके टेस्ट ऐप्लिकेशन को किसी दूसरे ऐप्लिकेशन को एक्सटेंड करने की ज़रूरत है, तो @CustomTestApplication के साथ नई क्लास या इंटरफ़ेस एनोटेट करें. साथ ही, उस बेस क्लास की वैल्यू पास करें जिसे जनरेट किए गए Hilt ऐप्लिकेशन को एक्सटेंड करना है.

@CustomTestApplication, Application क्लास जनरेट करेगा. यह क्लास, Hilt के साथ टेस्टिंग के लिए तैयार होगी. यह उस ऐप्लिकेशन को एक्सटेंड करती है जिसे आपने पैरामीटर के तौर पर पास किया है.

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

इस उदाहरण में, Hilt BaseApplication क्लास को बढ़ाने वाला Application नाम का HiltTestApplication_Application जनरेट करता है. आम तौर पर, जनरेट किए गए ऐप्लिकेशन का नाम, एनोटेट की गई क्लास का नाम होता है. इसके आखिर में _Application जोड़ा जाता है. आपको जनरेट किए गए Hilt टेस्ट ऐप्लिकेशन को इंस्ट्रुमेंटेड टेस्ट या Robolectric टेस्ट में चलाना होगा. इसके बारे में टेस्ट ऐप्लिकेशन में बताया गया है.

इंस्ट्रुमेंट किए गए टेस्ट में एक से ज़्यादा TestRule ऑब्जेक्ट

Compose के यूज़र इंटरफ़ेस (यूआई) टेस्ट में, HiltAndroidRule को Compose के टेस्ट के नियम के साथ पहले से ही जोड़ा जाता है. जैसे, createAndroidComposeRule. अगर आपके पास अतिरिक्त TestRule ऑब्जेक्ट हैं, तो पक्का करें कि HiltAndroidRule पहले चले. @Rule पर order एट्रिब्यूट का इस्तेमाल करके, एक्ज़ीक्यूशन के क्रम का एलान करें:

@HiltAndroidTest
class SettingsScreenTest {

  @get:Rule(order = 0)
  var hiltRule = HiltAndroidRule(this)

  @get:Rule(order = 1)
  val composeRule = createAndroidComposeRule<HiltTestActivity>()

  @get:Rule(order = 2)
  val otherRule = SomeOtherRule()

  // UI tests here.
}

इसके अलावा, नियमों को RuleChain के साथ रैप किया जा सकता है. इसमें HiltAndroidRule को बाहरी नियम के तौर पर रखा जाता है.

@HiltAndroidTest
class SettingsScreenTest {

  @get:Rule
  var rule = RuleChain.outerRule(HiltAndroidRule(this)).
        around(SettingsScreenTestRule(...))

  // UI tests here.
}

सिंगलटन कॉम्पोनेंट के उपलब्ध होने से पहले किसी एंट्री पॉइंट का इस्तेमाल करना

@EarlyEntryPoint एनोटेशन, तब काम आता है, जब Hilt टेस्ट में सिंगलटन कॉम्पोनेंट उपलब्ध होने से पहले, Hilt एंट्री पॉइंट बनाना होता है.

Hilt के दस्तावेज़ में, @EarlyEntryPoint के बारे में ज़्यादा जानकारी.

अन्य संसाधन

जांच के बारे में ज़्यादा जानने के लिए, यहां दिए गए अन्य संसाधन देखें:

दस्तावेज़

कॉन्टेंट देखता है