Hilt টেস্টিং গাইড (ভিউ)

ধারণা ও Jetpack Compose-এর প্রয়োগ

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

জাভা

@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 ডিপেন্ডেন্সি অন্তর্ভুক্ত করুন:

গ্ৰুভি

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

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

Kotlin

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

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

UI টেস্ট সেট-আপ

@HiltAndroidTest-এর সাথে Hilt ব্যবহার করে এমন যেকোনও UI টেস্ট আপনাকে অ্যানোটেট করতে হবে। এই অ্যানোটেশন প্রতিটি টেস্টের জন্য Hilt কম্পোনেন্ট তৈরি করার জন্য দায়ী।

এছাড়াও, আপনাকে টেস্ট ক্লাসে HiltAndroidRule যোগ করতে হবে। এটি কম্পোনেন্টের স্ট্যাটাস ম্যানেজ করে এবং আপনার টেস্টে ইনজেকশন পারফর্ম করতে ব্যবহার করা হয়:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // UI tests here.
}

জাভা

@HiltAndroidTest
public final class SettingsActivityTest {

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

  // UI tests here.
}

এরপর, আপনার টেস্টকে Application ক্লাস সম্পর্কে জানতে হবে যা Hilt আপনার জন্য অটোমেটিক তৈরি করে।

অ্যাপ্লিকেশন টেস্ট করুন

আপনাকে অবশ্যই এমন একটি Application অবজেক্টে Hilt ব্যবহার করে এমন ইন্সট্রুমেন্টেড টেস্ট চালাতে হবে যা 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)
    }
}

জাভা

// 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 ফাইলে এই টেস্ট রানার কনফিগার করুন। আপনি সম্পূর্ণ ক্লাসপাথ ব্যবহার করছেন কিনা তা নিশ্চিত করুন:

গ্ৰুভি

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 টেস্টে টেস্ট অ্যাপ্লিকেশন সেট করুন

আপনার UI লেয়ার পরীক্ষা করার জন্য 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.
}

জাভা

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

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

  // Robolectric tests here.
}

আপনি যদি 4.2-এর থেকে পুরনো Android Gradle প্লাগ-ইন ভার্সন ব্যবহার করেন, তাহলে আপনার মডিউলের build.gradle ফাইলে নিম্নলিখিত কনফিগারেশন প্রয়োগ করে লোকাল ইউনিট টেস্টে @AndroidEntryPoint ক্লাস ট্রান্সফর্ম করার সুবিধা চালু করুন:

গ্ৰুভি

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

Hilt ডকুমেন্টেশন থেকে enableTransformForLocalTests সম্পর্কে আরও তথ্য।

ফিচার পরীক্ষা করা

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

জাভা

@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
}

জাভা

@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
}

জাভা

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

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

একটি টেস্টে বাইন্ডিং পরিবর্তন করুন

সব টেস্টের পরিবর্তে একটি টেস্টে বাইন্ডিং পরিবর্তন করতে, @UninstallModules অ্যানোটেশন ব্যবহার করে টেস্ট থেকে Hilt মডিউল আনইনস্টল করুন এবং টেস্টের মধ্যে নতুন টেস্ট মডিউল তৈরি করুন।

আগের ভার্সনের AnalyticsService উদাহরণ অনুসরণ করে, টেস্ট ক্লাসে @UninstallModules অ্যানোটেশন ব্যবহার করে প্রোডাকশন মডিউল উপেক্ষা করার জন্য Hilt-কে বলে শুরু করুন:

Kotlin

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

জাভা

@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
  }

  ...
}

জাভা

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

  ...
}

জাভা

@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
}

জাভা

...
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

জাভা

@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

উদাহরণে, Hilt Application নামের একটি HiltTestApplication_Application জেনারেট করে যা BaseApplication ক্লাসকে এক্সটেন্ড করে। সাধারণত, জেনারেট করা অ্যাপ্লিকেশনের নাম হল _Application-এর সাথে যুক্ত করা অ্যানোটেটেড ক্লাসের নাম। টেস্ট অ্যাপ্লিকেশন-এ যেভাবে বর্ণনা করা হয়েছে সেই অনুযায়ী, আপনার ইনস্ট্রুমেন্টেড টেস্ট বা Robolectric টেস্ট-এ রান করার জন্য আপনাকে অবশ্যই জেনারেট করা Hilt টেস্ট অ্যাপ্লিকেশন সেট করতে হবে।

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

আপনার টেস্টে অন্যান্য TestRule অবজেক্ট থাকলে, সব নিয়ম একসাথে কাজ করছে কিনা তা নিশ্চিত করার একাধিক উপায় আছে।

আপনি নিম্নলিখিত উপায়ে নিয়মগুলি একসাথে র‍্যাপ করতে পারেন:

Kotlin

@HiltAndroidTest
class SettingsActivityTest {

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

  // UI tests here.
}

জাভা

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

জাভা

@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 কোড ব্যবহার করুন।