راهنمای تست هیلت

یکی از مزایای استفاده از چارچوب‌های تزریق وابستگی مانند 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(...)
  }
}

جاوا

@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.44'
    // ...with Kotlin.
    kaptTest 'com.google.dagger:hilt-android-compiler:2.44'
    // ...with Java.
    testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.44'


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

کاتلین

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


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

راه اندازی تست رابط کاربری

شما باید هر تست رابط کاربری که از Hilt استفاده می کند با @HiltAndroidTest حاشیه نویسی کنید. این حاشیه نویسی مسئول تولید اجزای Hilt برای هر تست است.

همچنین، باید HiltAndroidRule به کلاس تست اضافه کنید. وضعیت اجزا را مدیریت می کند و برای انجام تزریق در تست شما استفاده می شود:

کاتلین

@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 به طور خودکار برای شما تولید می کند، بداند.

نرم افزار تست

شما باید تست های ابزاری را اجرا کنید که از Hilt در یک شی Application که از Hilt پشتیبانی می کند استفاده می کند. این کتابخانه HiltTestApplication برای استفاده در تست ها ارائه می دهد. اگر آزمون‌های شما به برنامه پایه دیگری نیاز دارند، به برنامه سفارشی برای آزمایش‌ها مراجعه کنید.

شما باید برنامه آزمایشی خود را طوری تنظیم کنید که در تست های ابزاری یا روبولکتریک شما اجرا شود. دستورالعمل‌های زیر مختص Hilt نیستند، اما دستورالعمل‌های کلی در مورد نحوه تعیین یک برنامه سفارشی برای اجرا در آزمایش‌ها هستند.

برنامه آزمون را در تست های ابزار دقیق تنظیم کنید

برای استفاده از برنامه تست Hilt در تست های ابزاردار ، باید یک تست رانر جدید پیکربندی کنید. این باعث می شود که Hilt برای تمام تست های ابزاری در پروژه شما کار کند. مراحل زیر را انجام دهید:

  1. یک کلاس سفارشی ایجاد کنید که AndroidJUnitRunner در پوشه androidTest گسترش دهد.
  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)
    }
}

جاوا

// 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 خود پیکربندی کنید، همانطور که در راهنمای تست واحد ابزار دقیق توضیح داده شده است. مطمئن شوید که از classpath کامل استفاده می کنید:

شیار

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

کاتلین

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
برنامه تست را در تست های روبولکتریک تنظیم کنید

اگر از Robolectric برای آزمایش لایه UI خود استفاده می کنید، می توانید مشخص کنید که از کدام برنامه در فایل robolectric.properties استفاده کنید:

application = dagger.hilt.android.testing.HiltTestApplication

همچنین، می‌توانید با استفاده از حاشیه‌نویسی @Config Robolectric، برنامه را در هر آزمون به‌صورت جداگانه پیکربندی کنید:

کاتلین

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

اگر از نسخه Android Gradle Plugin پایین‌تر از 4.2 استفاده می‌کنید، با اعمال پیکربندی زیر در فایل build.gradle ماژول، تبدیل کلاس‌های @AndroidEntryPoint را در تست‌های واحد محلی فعال کنید:

شیار

hilt {
    enableTransformForLocalTests = true
}

کاتلین

hilt {
    enableTransformForLocalTests = true
}

اطلاعات بیشتر در مورد enableTransformForLocalTests در مستندات Hilt .

ویژگی های تست

هنگامی که Hilt آماده استفاده در تست های شما شد، می توانید از چندین ویژگی برای سفارشی کردن فرآیند تست استفاده کنید.

انواع تزریق در آزمایشات

برای تزریق انواع به یک تست، از @Inject برای تزریق فیلد استفاده کنید. برای اینکه به Hilt بگویید فیلدهای @Inject را پر کند، hiltRule.inject() را فراخوانی کنید.

نمونه زیر از یک تست ابزاری را ببینید:

کاتلین

@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 به شرح زیر اعلام می کند:

کاتلین

@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 در تست‌ها، یک ماژول جدید Hilt در پوشه test یا androidTest با وابستگی جعلی ایجاد کنید و آن را با @TestInstallIn حاشیه‌نویسی کنید. به جای آن همه تست‌های موجود در آن پوشه با وابستگی جعلی تزریق می‌شوند.

کاتلین

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

یک اتصال را در یک آزمایش جایگزین کنید

برای جایگزینی یک binding در یک تست به جای همه آزمایش‌ها، یک ماژول Hilt را با استفاده از حاشیه‌نویسی @UninstallModules حذف نصب کنید و یک ماژول تست جدید در داخل تست ایجاد کنید.

به دنبال مثال AnalyticsService از نسخه قبلی، با استفاده از حاشیه‌نویسی @UninstallModules در کلاس تست، به Hilt بگویید که ماژول تولید را نادیده بگیرد:

کاتلین

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

جاوا

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

بعد، شما باید اتصال را جایگزین کنید. یک ماژول جدید در کلاس تست ایجاد کنید که اتصال تست را تعریف می کند:

کاتلین

@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 در بخش بالا استفاده کنید. از طرف دیگر، می‌توانید اتصال تست را در ماژول test برای تست‌های Robolectric یا در ماژول androidTest برای تست‌های ابزاردار قرار دهید. توصیه این است که در صورت امکان از @TestInstallIn استفاده کنید.

مقید کردن مقادیر جدید

از حاشیه نویسی @BindValue استفاده کنید تا به راحتی فیلدهای آزمایش خود را به نمودار وابستگی Hilt متصل کنید. یک فیلد را با @BindValue حاشیه نویسی کنید و آن را تحت نوع فیلد اعلام شده با هر واجد شرایطی که برای آن فیلد وجود دارد محدود می شود.

در مثال AnalyticsService ، می توانید AnalyticsService با یک جعلی با استفاده از @BindValue جایگزین کنید:

کاتلین

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

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

  ...
}

جاوا

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

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

این کار هم جایگزینی یک binding و هم ارجاع به یک binding را در آزمون شما ساده می کند و به شما امکان می دهد هر دو را همزمان انجام دهید.

@BindValue با واجد شرایط و سایر حاشیه نویسی های آزمایشی کار می کند. به عنوان مثال، اگر از کتابخانه‌های آزمایشی مانند Mockito استفاده می‌کنید، می‌توانید از آن در تست Robolectric به صورت زیر استفاده کنید:

کاتلین

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

جاوا

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

  // Robolectric tests here
}

اگر نیاز به افزودن یک multibinding دارید، می توانید از حاشیه نویسی @BindValueIntoSet و @BindValueIntoMap به جای @BindValue استفاده کنید. @BindValueIntoMap از شما می‌خواهد که فیلد را با حاشیه‌نویسی کلید نقشه نیز حاشیه‌نویسی کنید.

موارد خاص

Hilt همچنین ویژگی هایی را برای پشتیبانی از موارد استفاده غیر استاندارد ارائه می دهد.

نرم افزار سفارشی برای تست

اگر نمی توانید از HiltTestApplication استفاده کنید زیرا برنامه آزمایشی شما باید برنامه دیگری را گسترش دهد، یک کلاس یا رابط جدید را با @CustomTestApplication حاشیه نویسی کنید و مقدار کلاس پایه مورد نظر را که می خواهید برنامه Hilt تولید شده گسترش دهد، ارسال کنید.

@CustomTestApplication یک کلاس Application آماده برای آزمایش با Hilt ایجاد می کند که برنامه ای را که به عنوان پارامتر ارسال کرده اید گسترش می دهد.

کاتلین

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

جاوا

@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

در مثال، Hilt یک Application به نام HiltTestApplication_Application تولید می کند که کلاس BaseApplication گسترش می دهد. به طور کلی، نام برنامه تولید شده، نام کلاس حاشیه نویسی است که با _Application ضمیمه شده است. شما باید برنامه تست هیلت تولید شده را طوری تنظیم کنید که در تست های ابزاردار یا تست های روبولکتریک همانطور که در برنامه تست توضیح داده شده است اجرا شود.

چندین شیء TestRule در آزمون ابزاری شما

اگر اشیاء TestRule دیگری در آزمون خود دارید، راه های متعددی برای اطمینان از کارکرد همه قوانین با هم وجود دارد.

می توانید قوانین را به صورت زیر در کنار هم قرار دهید:

کاتلین

@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 ابتدا اجرا شود، می توانید از هر دو قانون در یک سطح استفاده کنید. ترتیب اجرا را با استفاده از صفت order در حاشیه نویسی @Rule مشخص کنید. این فقط در نسخه JUnit 4.13 یا بالاتر کار می کند:

کاتلین

@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

استفاده از launchFragmentInContainer از کتابخانه androidx.fragment:fragment-testing با Hilt ممکن نیست، زیرا بر فعالیتی متکی است که با @AndroidEntryPoint حاشیه نویسی نشده است.

به جای آن از کد launchFragmentInHiltContainer از مخزن architecture-samples GitHub استفاده کنید.

قبل از در دسترس بودن کامپوننت singleton از یک نقطه ورودی استفاده کنید

حاشیه نویسی @EarlyEntryPoint یک دریچه فرار فراهم می کند زمانی که یک نقطه ورودی Hilt باید ایجاد شود قبل از اینکه جزء singleton در تست Hilt در دسترس باشد.

اطلاعات بیشتر در مورد @EarlyEntryPoint در مستندات Hilt .