Jedną z zalet korzystania z ramek wstrzykiwania zależności, takich jak Hilt, jest ułatwienie testowania kodu.
Testy jednostkowe
Hilt nie jest potrzebny do testów jednostkowych, ponieważ podczas testowania klasy, która korzysta z wstrzyknięcia konstruktora, nie musisz tworzyć instancji tej klasy za pomocą Hilta. Zamiast tego możesz bezpośrednio wywołać konstruktor klasy, przekazując fałszywe lub mockowane zależności, tak jak w przypadku konstruktora bez adnotacji:
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(...); } }
Kompleksowe testy
W przypadku testów integracji Hilt wstrzykuje zależności tak samo jak w środowisku produkcyjnym w kodzie. Testowanie za pomocą Hilt nie wymaga konserwacji, ponieważ Hilt automatycznie generuje nowy zestaw komponentów dla każdego testu.
Dodawanie zależności testowania
Aby używać Hilt do testowania, uwzględnij zależność hilt-android-testing
w parametrze
projekt:
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") }
Konfiguracja testu interfejsu
Do każdego testu interfejsu, który używa Hilt z funkcją @HiltAndroidTest
, musisz dodać adnotacje. Ten
odpowiada za generowanie komponentów Hilt dla każdego testu.
Musisz też dodać HiltAndroidRule
do klasy testowej. Zarządza
komponentów i służy do wykonywania w teście wstrzykiwania:
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. }
Następnie test musi znać klasę Application
obsługującą Hilt
generowane automatycznie.
Testuj aplikację
Musisz wykonać testy zinstrumentowane korzystające z Hilt w obiekcie Application
który obsługuje Hilt. Biblioteka udostępnia funkcje HiltTestApplication
do wykorzystania w testach.
Jeśli testy wymagają innej aplikacji podstawowej, zapoznaj się z artykułem Niestandardowa aplikacja do testów.
Należy ustawić aplikację testową tak, aby uruchamiała się w Testy lub Robolectric Nie można wykonywać tych czynności: dotyczące Hilt, ale zawierają ogólne wskazówki dotyczące do testowania.
Ustawienie aplikacji testowej w testach instrumentowanych
Korzystanie z aplikacji testowej Hilt w instrumentach testów, musisz skonfigurować nowy moduł uruchamiający test. Dzięki temu Hilt będzie działać we wszystkich testach zinstrumentowanych w projekcie. Wykonaj te kroki:
- Tworzenie niestandardowej klasy z rozszerzeniem
AndroidJUnitRunner
in folderandroidTest
. - Zastąp funkcję
newApplication
i przekaż nazwę wygenerowanego Aplikacja testowa 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); } }
Następnie skonfiguruj tego testującego w pliku Gradle zgodnie z opisem w przewodniku po testach jednostkowych z użyciem instrumentacji. Upewnij się, użyj pełnej ścieżki klasy:
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" } }
Ustaw aplikację testową w testach Robolectric
Jeśli do testowania warstwy UI używasz Robolectric, możesz określić, której aplikacji użyć w pliku robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
Możesz też skonfigurować aplikację w każdym teście pojedynczo,
za pomocą adnotacji @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. }
Jeśli używasz wtyczki Androida do obsługi Gradle w wersji starszej niż 4.2, włącz
przekształcenie klas @AndroidEntryPoint
w testy jednostkowe lokalnej przez zastosowanie
tę konfigurację w pliku build.gradle
modułu:
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Więcej informacji o enableTransformForLocalTests
znajdziesz w dokumentacji Hiltu.
Funkcje testowe
Gdy Hilt będzie gotowy do użycia w testach, możesz zacząć korzystać z kilku funkcji, dostosować proces testowania.
Wstawianie typów w testach
Aby wstrzykiwać typy do testu, używaj @Inject
do wstrzykiwania pól. Aby nakazywać Hiltowi
wypełnij pola @Inject
, wywołaj hiltRule.inject()
.
Zobacz przykład testu zinstruowanego:
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. } }
Zastępowanie wiązania
Jeśli chcesz wstrzyknąć fałszywy lub symulowany egzemplarz zależności, musisz powiedzieć Hiltowi, aby nie używał powiązania, którego używał w kodzie produkcyjnym, a zamiast tego użył innego. Aby zastąpić powiązanie, musisz zastąpić moduł, który zawiera powiązanie z modułem testowym, który zawiera wybrane powiązania. których można użyć w teście.
Na przykład załóżmy, że Twój kod produkcyjny deklaruje powiązanie dla
AnalyticsService
w następujący sposób:
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 ); }
Aby zastąpić w testach powiązanie AnalyticsService
, utwórz nowy moduł Hilt w folderze test
lub androidTest
z fałszywą zależnością i opatrz go adnotacją @TestInstallIn
. Wszystkie testy w tym folderze wstrzykiwane są przy użyciu fałszywych
zależność.
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 ); }
Zastępowanie powiązania w pojedynczym teście
Aby zastąpić powiązanie w pojedynczym teście zamiast we wszystkich testach, odinstaluj moduł Hilta z testu za pomocą adnotacji @UninstallModules
i utwórz nowy moduł testu w ramach testu.
Korzystając z przykładu AnalyticsService
z poprzedniej wersji, zacznij od polecenia
Jego kliknięcie powoduje zignorowanie modułu produkcyjnego za pomocą adnotacji @UninstallModules
na zajęciach testowych:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
Następnie musisz zastąpić powiązanie. Utwórz nowy moduł w klasie testu, który definiuje wiązanie testu:
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 ); } ... }
Spowoduje to zastąpienie tylko powiązania dla jednej klasy testów. Jeśli chcesz zastąpić
dla wszystkich klas testowych, użyj adnotacji @TestInstallIn
z tagu
powyżej. Możesz też umieścić testowe powiązanie w module test
w przypadku testów Robolectric, a w module androidTest
– do testów instrumentowanych.
Zalecamy, aby w miarę możliwości używać pola @TestInstallIn
.
Powiązanie nowych wartości
Użyj adnotacji @BindValue
, aby łatwo wiązać pola w teście z Hilt,
wykres zależności. Dodaj adnotację do pola @BindValue
, aby je objąć
zadeklarowany typ pola ze wszystkimi kwalifikatorami, które są w nim obecne.
W przykładzie AnalyticsService
możesz zamienić element AnalyticsService
na
fałszywka, używając @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(); ... }
Dzięki temu możesz zastąpić powiązanie i odwoływać się do niego w testach jednocześnie.
@BindValue
działa z kwalifikatorami i innymi adnotacjami testowymi. Przykład:
jeśli używasz bibliotek testowych takich jak
Mockito, możesz go użyć w
Jak wygląda test Roboelektryczny:
Kotlin
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Java
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable; // Robolectric tests here }
Jeśli musisz dodać wiązanie wieloelementowe, możesz użyć adnotacji @BindValueIntoSet
i @BindValueIntoMap
zamiast @BindValue
. @BindValueIntoMap
wymaga dodania adnotacji do pola
z adnotacją klucza mapy.
Przypadki szczególne
Hilt udostępnia też funkcje, które ułatwiają obsługę niestandardowych przypadków użycia.
Niestandardowa aplikacja do testów
Jeśli nie możesz użyć usługi HiltTestApplication
, ponieważ aplikacja testowa musi:
rozszerzania innej aplikacji, dodawania adnotacji do nowej klasy lub interfejsu
@CustomTestApplication
, przekazując wartość klasy bazowej, którą chcesz ustawić
Wygenerowano aplikację Hilt do rozszerzenia.
@CustomTestApplication
wygeneruje klasę Application
gotową do testowania za pomocą Hilt, która rozszerza aplikację przekazaną jako parametr.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
W przykładzie Hilt generuje pole Application
o nazwie
HiltTestApplication_Application
rozszerzający klasę BaseApplication
. W
ogólny, nazwa wygenerowanej aplikacji jest nazwą z adnotacjami
zajęcia z dodanym elementem _Application
. Musisz ustawić wygenerowany test Hilt
aplikację do uruchomienia w testach instrumentalnych lub
Testy Robolectric, zgodnie z opisem w sekcji Testy
aplikacji.
Wiele obiektów TestRule w teście instrumentalnym
Jeśli masz w teście inne obiekty TestRule
, możesz to zrobić na kilka sposobów.
aby upewnić się, że wszystkie reguły będą ze sobą działać.
Reguły możesz owinąć w ten sposób:
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. }
Możesz też używać obu reguł na tym samym poziomie, o ile
Komponent HiltAndroidRule
jest uruchamiany jako pierwszy. Określ kolejność wykonywania za pomocą atrybutu order
w adnotacji @Rule
. Działa to tylko w wersji JUnit
4.13 lub nowszy:
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
Nie można używać funkcji launchFragmentInContainer
z biblioteki androidx.fragment:fragment-testing
w Hilt, ponieważ korzysta ona z aktywności, która nie jest opatrzona adnotacjami @AndroidEntryPoint
.
Użyj
launchFragmentInHiltContainer
z interfejsu
architecture-samples
GitHub
.
Używanie punktu wejścia przed udostępnieniem komponentu singleton
Adnotacja @EarlyEntryPoint
zapewnia dostęp awaryjny, gdy wpis Hilt
aby komponent singleton był dostępny w
Test z ręką.
Więcej informacji o @EarlyEntryPoint
w
Dokumentacja Hilt