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