แนวคิดและการใช้งาน Jetpack Compose
ประโยชน์อย่างหนึ่งของการใช้เฟรมเวิร์กการแทรกทรัพยากร Dependency เช่น Hilt คือช่วยให้ทดสอบโค้ดได้ง่ายขึ้น
Unit Test
Hilt ไม่จำเป็นสำหรับการทำ Unit Test เนื่องจากเมื่อทดสอบคลาสที่ใช้การแทรกเครื่องมือสร้าง คุณไม่จำเป็นต้องใช้ 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
คุณต้องใส่คำอธิบายประกอบการทดสอบ UI ที่ใช้ Hilt ด้วย @HiltAndroidTest คำอธิบายประกอบนี้มีหน้าที่สร้างคอมโพเนนต์ 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 ในการทดสอบที่มีการวัดคุม คุณต้องกำหนดค่า Test Runner ใหม่ ซึ่งจะทำให้ 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); } }
ถัดไป ให้กำหนดค่า Test Runner นี้ในไฟล์ Gradle ตามที่อธิบายไว้ในคู่มือการทำ Unit Test ที่ใช้เครื่องมือวัด ตรวจสอบว่าคุณใช้ 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 ใน Unit Test ในเครื่องโดยใช้การกำหนดค่าต่อไปนี้ในไฟล์ 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 และสร้างโมดูลการทดสอบใหม่ภายในการทดสอบ
จากตัวอย่าง 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 จากส่วนด้านบน หรือจะใส่การเชื่อมโยงการทดสอบในโมดูล 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 }
หากต้องการเพิ่ม multibinding คุณสามารถใช้คำอธิบายประกอบ @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
โปรดใช้โค้ด launchFragmentInHiltContainer จากที่เก็บ GitHub architecture-samples แทน