হিল্ট টেস্টিং গাইড

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 থেকে কম্পোজেবলগুলিতে স্টেট কীভাবে প্রবাহিত হয় সে সম্পর্কে তথ্যের জন্য, "State and Jetpack Compose" এবং "Where to hoist state" দেখুন।

এন্ড-টু-এন্ড পরীক্ষা

ইন্টিগ্রেশন টেস্টের জন্য, হিল্ট আপনার প্রোডাকশন কোডের মতোই ডিপেন্ডেন্সিগুলো যুক্ত করে। হিল্ট দিয়ে টেস্টিং করার জন্য কোনো রক্ষণাবেক্ষণের প্রয়োজন হয় না, কারণ এটি প্রতিটি টেস্টের জন্য স্বয়ংক্রিয়ভাবে এক নতুন সেট কম্পোনেন্ট তৈরি করে।

টেস্টিং নির্ভরতা যোগ করা

আপনার টেস্টে 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")

}

UI টেস্ট সেটআপ

যেসব UI টেস্টে 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 সমর্থন করে এমন একটি Application অবজেক্টে Hilt ব্যবহার করে এমন ইন্সট্রুমেন্টেড টেস্টগুলো চালাতে হবে। লাইব্রেরিটি টেস্টে ব্যবহারের জন্য HiltTestApplication প্রদান করে। যদি আপনার টেস্টের জন্য একটি ভিন্ন বেস অ্যাপ্লিকেশনের প্রয়োজন হয়, তবে টেস্টের জন্য কাস্টম অ্যাপ্লিকেশন দেখুন।

আপনার ইন্সট্রুমেন্টেড টেস্ট অথবা রোবোইলেকট্রিক টেস্টে চালানোর জন্য আপনাকে অবশ্যই আপনার টেস্ট অ্যাপ্লিকেশনটি সেট করতে হবে। নিম্নলিখিত নির্দেশাবলী হিল্ট-এর জন্য নির্দিষ্ট নয়, বরং টেস্টে চালানোর জন্য একটি কাস্টম অ্যাপ্লিকেশন কীভাবে নির্দিষ্ট করতে হয়, সে সম্পর্কে এগুলি সাধারণ নির্দেশিকা।

ইনস্ট্রুমেন্টেড টেস্টে টেস্ট অ্যাপ্লিকেশনটি সেট করুন।

ইন্সট্রুমেন্টেড টেস্টে 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)
    }
}

এরপর, ইন্সট্রুমেন্টেড ইউনিট টেস্ট গাইডে বর্ণিত পদ্ধতি অনুযায়ী আপনার গ্রেডল ফাইলে এই টেস্ট রানারটি কনফিগার করুন। সম্পূর্ণ ক্লাসপাথ ব্যবহার করছেন কিনা তা নিশ্চিত করুন:

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
রোবোইলেকট্রিক টেস্টে টেস্ট অ্যাপ্লিকেশনটি সেট করুন।

আপনি যদি আপনার UI লেয়ার পরীক্ষা করার জন্য Robolectric ব্যবহার করেন, তাহলে robolectric.properties ফাইলে কোন অ্যাপ্লিকেশনটি ব্যবহার করবেন তা নির্দিষ্ট করে দিতে পারেন:

application = dagger.hilt.android.testing.HiltTestApplication

বিকল্পভাবে, আপনি রোবোইলেকট্রিকের @Config অ্যানোটেশন ব্যবহার করে প্রতিটি টেস্টে অ্যাপ্লিকেশনটি আলাদাভাবে কনফিগার করতে পারেন:

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

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}

পরীক্ষার বৈশিষ্ট্য

আপনার টেস্টে ব্যবহারের জন্য হিল্ট প্রস্তুত হয়ে গেলে, আপনি টেস্টিং প্রক্রিয়াটিকে নিজের মতো করে সাজিয়ে নিতে এর বিভিন্ন ফিচার ব্যবহার করতে পারেন।

পরীক্ষায় প্রকারগুলি ইনজেক্ট করুন

কোনো টেস্টে টাইপ যুক্ত করতে, ফিল্ড ইনজেকশনের জন্য @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 অ্যানোটেশনটি ব্যবহার করুন। বিকল্পভাবে, আপনি রোবোইলেকট্রিক টেস্টের জন্য test মডিউলে, অথবা ইন্সট্রুমেন্টেড টেস্টের জন্য androidTest মডিউলে টেস্ট বাইন্ডিং রাখতে পারেন। যখনই সম্ভব @TestInstallIn ব্যবহার করার পরামর্শ দেওয়া হয়।

নতুন মূল্যবোধকে আবদ্ধ করা

আপনার টেস্টের ফিল্ডগুলোকে Hilt ডিপেন্ডেন্সি গ্রাফে সহজে বাইন্ড করতে @BindValue অ্যানোটেশনটি ব্যবহার করুন। কোনো ফিল্ডকে @BindValue দিয়ে অ্যানোটেট করলে, সেটি ঘোষিত ফিল্ড টাইপের অধীনে এবং সেই ফিল্ডের জন্য উপস্থিত যেকোনো কোয়ালিফায়ারসহ বাইন্ড হয়ে যাবে।

AnalyticsService উদাহরণে, আপনি @BindValue ব্যবহার করে AnalyticsService একটি নকল মান দিয়ে প্রতিস্থাপন করতে পারেন:

@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 ব্যবহার করার জন্য আপনাকে ফিল্ডটিকে একটি ম্যাপ কী অ্যানোটেশন দিয়েও চিহ্নিত করতে হবে।

বিশেষ ক্ষেত্রে

হিল্ট অপ্রচলিত ব্যবহারের ক্ষেত্রগুলোকে সমর্থন করার জন্য বিভিন্ন বৈশিষ্ট্যও প্রদান করে।

পরীক্ষার জন্য কাস্টম অ্যাপ্লিকেশন

যদি আপনার টেস্ট অ্যাপ্লিকেশনটিকে অন্য কোনো অ্যাপ্লিকেশন এক্সটেন্ড করতে হয় এবং সেই কারণে আপনি HiltTestApplication ব্যবহার করতে না পারেন, তাহলে একটি নতুন ক্লাস বা ইন্টারফেসকে @CustomTestApplication দিয়ে অ্যানোটেট করুন এবং যে বেস ক্লাসটিকে আপনি তৈরি করা Hilt অ্যাপ্লিকেশনটির জন্য এক্সটেন্ড করতে চান, তার ভ্যালুটি পাস করে দিন।

@CustomTestApplication একটি Application ক্লাস তৈরি করবে যা Hilt দিয়ে পরীক্ষার জন্য প্রস্তুত এবং এটি আপনার প্যারামিটার হিসেবে দেওয়া অ্যাপ্লিকেশনটিকে এক্সটেন্ড করবে।

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

উদাহরণটিতে, Hilt, HiltTestApplication_Application নামের একটি Application তৈরি করে যা BaseApplication ক্লাসকে এক্সটেন্ড করে। সাধারণত, তৈরি হওয়া অ্যাপ্লিকেশনটির নাম হলো অ্যানোটেড ক্লাসের নামের শেষে _Application যুক্ত করা। টেস্ট অ্যাপ্লিকেশন অংশে বর্ণিত পদ্ধতি অনুযায়ী, আপনাকে অবশ্যই তৈরি হওয়া Hilt টেস্ট অ্যাপ্লিকেশনটিকে আপনার ইন্সট্রুমেন্টেড টেস্ট বা রোবোইলেকট্রিক টেস্টে চালানোর জন্য সেট করতে হবে।

আপনার ইনস্ট্রুমেন্টেড টেস্টে একাধিক TestRule অবজেক্ট

Compose UI টেস্টগুলো ইতিমধ্যেই HiltAndroidRule createAndroidComposeRule মতো একটি Compose টেস্ট রুলের সাথে একত্রিত করে। আপনার যদি অতিরিক্ত 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 অ্যানোটেশনটি একটি বিকল্প পথ প্রদান করে।

হিল্ট ডকুমেন্টেশনে @EarlyEntryPoint সম্পর্কে আরও তথ্য রয়েছে।

অতিরিক্ত সম্পদ

পরীক্ষা সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত উৎসগুলো দেখুন:

ডকুমেন্টেশন

বিষয়বস্তু দেখুন