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

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

यूनिट टेस्ट

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

Kotlin

@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(...)
  }
}

Java

@ActivityScope
public class AnalyticsAdapter {

  private final AnalyticsService analyticsService;

  @Inject
  AnalyticsAdapter(AnalyticsService analyticsService) {
    this.analyticsService = analyticsService;
  }
}

public final class AnalyticsAdapterTest {

  @Test
  public void happyPath() {
    // You don't need Hilt to create an instance of AnalyticsAdapter.
    // You can pass a fake or mock AnalyticsService.
    AnalyticsAdapter adapter = new AnalyticsAdapter(fakeAnalyticsService);
    assertEquals(...);
  }
}

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

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

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

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

Groovy

dependencies {
    // For Robolectric tests.
    testImplementation 'com.google.dagger:hilt-android-testing:2.51.1'
    // ...with Kotlin.
    kaptTest 'com.google.dagger:hilt-android-compiler:2.51.1'
    // ...with Java.
    testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.51.1'


    // For instrumented tests.
    androidTestImplementation 'com.google.dagger:hilt-android-testing:2.51.1'
    // ...with Kotlin.
    kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.51.1'
    // ...with Java.
    androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.51.1'
}

Kotlin

dependencies {
    // For Robolectric tests.
    testImplementation("com.google.dagger:hilt-android-testing:2.51.1")
    // ...with Kotlin.
    kaptTest("com.google.dagger:hilt-android-compiler:2.51.1")
    // ...with Java.
    testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.51.1")


    // For instrumented tests.
    androidTestImplementation("com.google.dagger:hilt-android-testing:2.51.1")
    // ...with Kotlin.
    kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.51.1")
    // ...with Java.
    androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.51.1")
}

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

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

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

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // UI tests here.
}

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

ऐप्लिकेशन की जांच करना

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

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

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

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

  1. androidTest फ़ोल्डर में, AndroidJUnitRunner को एक्सटेंड करने वाली कस्टम क्लास बनाएं.
  2. newApplication फ़ंक्शन को बदलें और जनरेट किए गए Hilt टेस्ट ऐप्लिकेशन का नाम डालें.

Kotlin

// 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)
    }
}

Java

// A custom runner to set up the instrumented application class for tests.
public final class CustomTestRunner extends AndroidJUnitRunner {

  @Override
  public Application newApplication(ClassLoader cl, String className, Context context)
      throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    return super.newApplication(cl, HiltTestApplication.class.getName(), context);
  }
}

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

Groovy

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner "com.example.android.dagger.CustomTestRunner"
    }
}

Kotlin

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 एनोटेशन का इस्तेमाल करके, हर टेस्ट के लिए ऐप्लिकेशन को अलग-अलग कॉन्फ़िगर किया जा सकता है:

Kotlin

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

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}

Java

@HiltAndroidTest
@Config(application = HiltTestApplication.class)
class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // Robolectric tests here.
}

अगर Android Gradle प्लग इन के 4.2 से पहले के वर्शन का इस्तेमाल किया जा रहा है, तो अपने मॉड्यूल की build.gradle फ़ाइल में यह कॉन्फ़िगरेशन लागू करके, लोकल यूनिट टेस्ट में @AndroidEntryPoint क्लास को ट्रांसफ़ॉर्म करने की सुविधा चालू करें:

Groovy

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

enableTransformForLocalTests के बारे में ज़्यादा जानकारी के लिए, Hilt के दस्तावेज़ देखें.

जांच करने की सुविधाएं

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

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

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

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

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  @Inject
  lateinit var analyticsAdapter: AnalyticsAdapter

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

  @Test
  fun `happy path`() {
    // Can already use analyticsAdapter here.
  }
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  @Inject AnalyticsAdapter analyticsAdapter;

  @Before
  public void init() {
    hiltRule.inject();
  }

  @Test
  public void happyPath() {
    // Can already use analyticsAdapter here.
  }
}

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

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

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

Kotlin

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

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

Java

@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

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

Kotlin

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

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

Java

@Module
@TestInstallIn(
    components = SingletonComponent.class,
    replaces = AnalyticsModule.class
)
public abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    FakeAnalyticsService fakeAnalyticsService
  );
}

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

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

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

Kotlin

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

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
public final class SettingsActivityTest { ... }

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

Kotlin

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

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

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

  ...
}

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
public final class SettingsActivityTest {

  @Module
  @InstallIn(SingletonComponent.class)
  public abstract class TestModule {

    @Singleton
    @Binds
    public abstract AnalyticsService bindAnalyticsService(
      FakeAnalyticsService fakeAnalyticsService
    );
  }
  ...
}

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

नई वैल्यू बांधना

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

AnalyticsService के उदाहरण में, @BindValue का इस्तेमाल करके AnalyticsService को किसी फ़ेक वैल्यू से बदला जा सकता है:

Kotlin

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

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

  ...
}

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
class SettingsActivityTest {

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

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

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

Kotlin

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

Java

...
class SettingsActivityTest {
  ...
  @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable;

  // Robolectric tests here
}

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

विशेष मामले

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

जांच के लिए कस्टम ऐप्लिकेशन

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

@CustomTestApplication, Hilt की मदद से जांच के लिए तैयार Application क्लास जनरेट करेगा. यह क्लास, पैरामीटर के तौर पर पास किए गए ऐप्लिकेशन को बढ़ाती है.

Kotlin

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

Java

@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

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

आपके इंस्ट्रूमेंट किए गए टेस्ट में कई TestRule ऑब्जेक्ट

अगर आपके टेस्ट में अन्य TestRule ऑब्जेक्ट हैं, तो यह पक्का करने के कई तरीके हैं कि सभी नियम एक साथ काम करें.

नियमों को एक साथ इस तरह रैप किया जा सकता है:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

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

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public RuleChain rule = RuleChain.outerRule(new HiltAndroidRule(this))
        .around(new SettingsActivityTestRule(...));

  // UI tests here.
}

इसके अलावा, दोनों नियमों का इस्तेमाल एक ही लेवल पर किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि पहले HiltAndroidRule लागू हो. @Rule एनोटेशन में order एट्रिब्यूट का इस्तेमाल करके, लागू करने का क्रम तय करें. यह सिर्फ़ JUnit के 4.13 या इसके बाद के वर्शन में काम करता है:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

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

  @get:Rule(order = 1)
  var settingsActivityTestRule = SettingsActivityTestRule(...)

  // UI tests here.
}

Java

@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule(order = 0)
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  @Rule(order = 1)
  public SettingsActivityTestRule settingsActivityTestRule = new SettingsActivityTestRule(...);

  // UI tests here.
}

launchFragmentInContainer

Hilt के साथ androidx.fragment:fragment-testing लाइब्रेरी से launchFragmentInContainer का इस्तेमाल नहीं किया जा सकता, क्योंकि यह ऐसी गतिविधि पर निर्भर करती है जिसे @AndroidEntryPoint से एनोटेट नहीं किया गया है.

इसके बजाय, architecture-samples GitHub रिपॉज़िटरी में मौजूद launchFragmentInHiltContainer कोड का इस्तेमाल करें.

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

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

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