ข้อดีอย่างหนึ่งของการใช้เฟรมเวิร์กการแทรกทรัพยากร Dependency เช่น Hilt คือช่วยให้การทดสอบโค้ดง่ายขึ้น
การทดสอบหน่วย
คุณไม่จำเป็นต้องใช้ Hilt สำหรับการทดสอบหน่วย เนื่องจากเมื่อทดสอบคลาสที่ใช้การแทรกตัวสร้าง คุณไม่จำเป็นต้องใช้ Hilt เพื่อสร้างอินสแตนซ์ของคลาสดังกล่าว แต่คุณสามารถเรียกใช้เครื่องมือสร้างคลาสได้โดยตรงโดยส่งทรัพยากร Dependency ปลอมหรือจำลองเข้าไป เช่นเดียวกับที่คุณทำหากเครื่องมือสร้างไม่มีคำอธิบายประกอบ
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 จะแทรกทรัพยากร Dependency เหมือนกับที่ทำในโค้ดเวอร์ชันที่ใช้งานจริง การทดสอบด้วย Hilt ไม่จำเป็นต้องมีการบำรุงรักษาเนื่องจาก Hilt จะสร้างชุดคอมโพเนนต์ใหม่สำหรับการทดสอบแต่ละครั้งโดยอัตโนมัติ
การเพิ่มทรัพยากร Dependency สำหรับการทดสอบ
หากต้องการใช้ Hilt ในการทดสอบ ให้ใส่ทรัพยากร Dependency 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 ในการทดสอบ UI ที่ใช้ 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 ในการทดสอบที่ใช้เครื่องมือ instrumented tests คุณต้องกำหนดค่าตัวเรียกใช้การทดสอบใหม่ ซึ่งจะทำให้ Hilt ทำงานกับการทดสอบที่ใช้เครื่องมือทั้งหมดในโปรเจ็กต์ ทำตามขั้นตอนต่อไปนี้
- สร้างคลาสที่กำหนดเองซึ่งขยาย
AndroidJUnitRunnerใน โฟลเดอร์androidTest - ลบล้างฟังก์ชัน
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 ตามที่อธิบายไว้ใน คู่มือการทดสอบหน่วยที่ใช้เครื่องมือ ตรวจสอบว่าคุณใช้ classpath แบบเต็ม
ดึงดูด
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 เพื่อทดสอบเลเยอร์ UI คุณสามารถระบุแอปพลิเคชันที่จะใช้ในไฟล์ robolectric.properties ได้
application = dagger.hilt.android.testing.HiltTestApplication
หรือคุณจะกำหนดค่าแอปพลิเคชันในการทดสอบแต่ละครั้งแยกกันโดยใช้คำอธิบายประกอบ @Config ของ Robolectric ก็ได้
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 ให้เปิดใช้การแปลงคลาส @AndroidEntryPoint ในการทดสอบหน่วยภายในโดยใช้การกำหนดค่าต่อไปนี้ในไฟล์ build.gradle ของโมดูล
ดึงดูด
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. } }
แทนที่การเชื่อมโยง
หากต้องการแทรกอินสแตนซ์ปลอมหรือจำลองของทรัพยากร Dependency คุณต้องบอกให้ 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 ในการทดสอบ ให้สร้างโมดูล Hilt ใหม่ในโฟลเดอร์ test หรือ androidTest ที่มีทรัพยากร Dependency ปลอมและใส่คำอธิบายประกอบ @TestInstallIn ระบบจะแทรกทรัพยากร Dependency ปลอมในการทดสอบทั้งหมดในโฟลเดอร์นั้นแทน
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 ); }
แทนที่การเชื่อมโยงในการทดสอบครั้งเดียว
หากต้องการแทนที่การเชื่อมโยงในการทดสอบครั้งเดียวแทนที่จะเป็นการทดสอบทั้งหมด ให้ถอนการติดตั้งโมดูล Hilt จากการทดสอบโดยใช้คำอธิบายประกอบ @UninstallModules และสร้างโมดูลทดสอบใหม่ในการทดสอบ
เริ่มจากบอกให้ Hilt ละเว้นโมดูลเวอร์ชันที่ใช้งานจริงโดยใช้คำอธิบายประกอบ @UninstallModules ในคลาสทดสอบตามตัวอย่าง AnalyticsService จากเวอร์ชันก่อนหน้า
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 จากส่วนด้านบน หรือคุณจะใส่การเชื่อมโยงทดสอบในโมดูล test สำหรับการทดสอบ Robolectric หรือในโมดูล androidTest สำหรับการทดสอบที่ใช้เครื่องมือก็ได้
เราขอแนะนำให้ใช้ @TestInstallIn ทุกครั้งที่ทำได้
การเชื่อมโยงค่าใหม่
ใช้คำอธิบายประกอบ @BindValue เพื่อเชื่อมโยงฟิลด์ในการทดสอบเข้ากับกราฟทรัพยากร Dependency ของ Hilt ได้อย่างง่ายดาย ใส่คำอธิบายประกอบ @BindValue ในฟิลด์ แล้วระบบจะเชื่อมโยงฟิลด์ดังกล่าวภายใต้ประเภทฟิลด์ที่ประกาศไว้พร้อมกับตัวระบุที่แสดงสำหรับฟิลด์นั้น
ในตัวอย่าง AnalyticsService คุณสามารถแทนที่ AnalyticsService ด้วยค่าปลอมโดยใช้ @BindValue ได้
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest class SettingsActivityTest { @BindValue AnalyticsService analyticsService = FakeAnalyticsService(); ... }
การดำเนินการนี้จะช่วยลดความซับซ้อนทั้งในการแทนที่การเชื่อมโยงและการอ้างอิงการเชื่อมโยงในการทดสอบโดยให้คุณทำทั้ง 2 อย่างได้พร้อมกัน
@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 }
หากต้องการเพิ่มการเชื่อมโยงหลายรายการ คุณสามารถใช้คำอธิบายประกอบ @BindValueIntoSet และ @BindValueIntoMap แทน @BindValue ได้ @BindValueIntoMap กำหนดให้คุณต้องใส่คำอธิบายประกอบฟิลด์ด้วยคำอธิบายประกอบคีย์ของแผนที่ด้วย
กรณีพิเศษ
นอกจากนี้ Hilt ยังมีฟีเจอร์ต่างๆ เพื่อรองรับกรณีการใช้งานที่ไม่เป็นไปตามมาตรฐานด้วย
แอปพลิเคชันที่กำหนดเองสำหรับการทดสอบ
หากคุณใช้ HiltTestApplication ไม่ได้เนื่องจากแอปพลิเคชันทดสอบต้องขยายแอปพลิเคชันอื่น ให้ใส่คำอธิบายประกอบ @CustomTestApplication ในคลาสหรืออินเทอร์เฟซใหม่ โดยส่งค่าของคลาสพื้นฐานที่คุณต้องการให้แอปพลิเคชัน Hilt ที่สร้างขึ้นขยาย
@CustomTestApplication จะสร้างคลาส Application ที่พร้อมสำหรับการทดสอบ
ด้วย Hilt ซึ่งขยายแอปพลิเคชันที่คุณส่งเป็นพารามิเตอร์
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
ในตัวอย่างนี้ Hilt จะสร้าง 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. }
หรือคุณจะใช้กฎทั้ง 2 ข้อในระดับเดียวกันก็ได้ตราบใดที่ HiltAndroidRule ทำงานก่อน ระบุลำดับการดำเนินการโดยใช้แอตทริบิวต์ order ในคำอธิบายประกอบ @Rule ซึ่งจะใช้ได้ใน 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
คุณใช้ launchFragmentInContainer จากไลบรารี androidx.fragment:fragment-testing กับ Hilt ไม่ได้ เนื่องจากฟังก์ชันนี้ต้องอาศัยกิจกรรมที่ไม่ได้ใส่คำอธิบายประกอบ @AndroidEntryPoint
ให้ใช้โค้ดจากที่เก็บ GitHub แทนlaunchFragmentInHiltContainerarchitecture-samples
ใช้จุดแรกเข้าก่อนที่คอมโพเนนต์ Singleton จะพร้อมใช้งาน
คำอธิบายประกอบ @EarlyEntryPoint มีไว้ให้ใช้ในกรณีที่ต้องสร้างจุดเริ่มต้น Hilt ก่อนที่คอมโพเนนต์ Singleton จะพร้อมใช้งานในการทดสอบ Hilt
ดูข้อมูลเพิ่มเติมเกี่ยวกับ @EarlyEntryPoint ใน
เอกสารประกอบของ Hilt