ข้อดีอย่างหนึ่งของการใช้เฟรมเวิร์กการแทรกทรัพยากร Dependency เช่น 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(...) } }
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 จะแทรกการอ้างอิงเหมือนกับในโค้ดเวอร์ชันที่ใช้งานจริง การทดสอบด้วย Hilt ไม่ต้องมีการบำรุงรักษาเนื่องจาก Hilt จะสร้างชุดคอมโพเนนต์ใหม่สำหรับการทดสอบแต่ละครั้งโดยอัตโนมัติ
การเพิ่มทรัพยากร Dependency ของการทดสอบ
หากต้องการใช้ Hilt ในการทดสอบ ให้รวมการอ้างอิง hilt-android-testing
ในโปรเจ็กต์
Groovy
dependencies { // For Robolectric tests. testImplementation 'com.google.dagger:hilt-android-testing:2.56.2' // ...with Kotlin. kaptTest 'com.google.dagger:hilt-android-compiler:2.56.2' // ...with Java. testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.56.2' // For instrumented tests. androidTestImplementation 'com.google.dagger:hilt-android-testing:2.56.2' // ...with Kotlin. kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.56.2' // ...with Java. androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.56.2' }
Kotlin
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.56.2") // ...with Kotlin. kaptTest("com.google.dagger:hilt-android-compiler:2.56.2") // ...with Java. testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.56.2") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.56.2") // ...with Kotlin. kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.56.2") // ...with Java. androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.56.2") }
การตั้งค่าการทดสอบ 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); } }
จากนั้นกำหนดค่าโปรแกรมเรียกใช้การทดสอบนี้ในไฟล์ Gradle ตามที่อธิบายไว้ใน คู่มือการทดสอบหน่วยที่มีการวัดผล ตรวจสอบว่า คุณใช้ classpath แบบเต็ม
Groovy
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
ของโมดูล
Groovy
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
ระบบจะแทรกการทดสอบทั้งหมดในโฟลเดอร์นั้นด้วยการขึ้นต่อปลอมแทน
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
เพื่อเชื่อมโยงฟิลด์ในการทดสอบกับกราฟการอ้างอิงของ Hilt
ได้อย่างง่ายดาย ใส่คำอธิบายประกอบในช่องด้วย @BindValue
แล้วระบบจะเชื่อมโยงช่องดังกล่าวภายใต้
ประเภทช่องที่ประกาศพร้อมตัวระบุที่มีอยู่สำหรับช่องนั้น
ในAnalyticsService
ตัวอย่าง คุณสามารถแทนที่ AnalyticsService
ด้วย a
fake โดยใช้ @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
โปรดใช้โค้ด
launchFragmentInHiltContainer
จากที่เก็บ
architecture-samples
GitHub
แทน
ใช้จุดแรกเข้าก่อนที่คอมโพเนนต์ Singleton จะพร้อมใช้งาน
คำอธิบายประกอบ @EarlyEntryPoint
เป็นทางออกในกรณีที่ต้องสร้างจุดแรกเข้าของ Hilt
ก่อนที่คอมโพเนนต์ Singleton จะพร้อมใช้งานในการทดสอบ Hilt
ดูข้อมูลเพิ่มเติมเกี่ยวกับ @EarlyEntryPoint
ได้ใน
เอกสารประกอบของ Hilt