Jedną z zalet korzystania z platform wstrzykiwania zależności, takich jak Hilt, jest to, ułatwia testowanie kodu.
Testy jednostkowe
Hilt nie jest wymagany w przypadku testów jednostkowych, ponieważ przy testowaniu klasy, która używa argumentu konstruktora, nie musisz używać Hilt do tworzenia instancji. Zamiast tego możesz bezpośrednio wywołać konstruktor klas, przekazując wartość „fałsz” lub „imitację” 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 powoduje wygenerowanie nowego zestawu komponentów dla każdego testu.
Dodaję zależności testowe
Aby używać Hilt do testowania, uwzględnij zależność hilt-android-testing
w parametrze
projekt:
Odlotowe
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. Ta adnotacja odpowiada za generowanie komponentów Hilta 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żeli testy wymagają innej aplikacji podstawowej, zapoznaj się z sekcją Aplikacja niestandardowa do
.
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 ten mechanizm uruchamiania testów w pliku Gradle zgodnie z opisem w test jednostkowy instrumentów . Upewnij się, użyj pełnej ścieżki klasy:
Odlotowe
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 używasz Robolectric do testowania warstwy interfejsu, możesz określić, która aplikacja
do użycia 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:
Odlotowe
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Więcej informacji o enableTransformForLocalTests
znajdziesz w Hilt
dokumentacji.
Funkcje testowe
Gdy Hilt będzie gotowy do użycia w testach, możesz zacząć korzystać z kilku funkcji, dostosować proces testowania.
Wstrzykiwanie 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 ten przykład testu instrumentowanego:
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łszywą lub pozorowaną instancję zależności, musisz przekazać Nie należy używać powiązania użytego w kodzie produkcyjnym oraz inny plik. 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.
Załóżmy na przykład, że kod produkcyjny deklaruje ograniczenie dotyczące AnalyticsService
w ten 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ć powiązanie AnalyticsService
w testach, utwórz nowy moduł Hilt
folder test
lub androidTest
z fałszywą zależnością i dodaj do niej adnotacje
dzięki @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, a nie we wszystkich testach, odinstaluj Hilt
z testu przy użyciu adnotacji @UninstallModules
i utwórz nowy moduł
z modułu testowego.
Korzystając z przykładu AnalyticsService
z poprzedniej wersji, zacznij od polecenia
Wskazuje, że moduł produkcyjny jest ignorowany 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 ramach klasy testowej definiujące powią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 ); } ... }
Zastępuje to powiązanie tylko dla jednej klasy testowej. 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
Aby łatwo łączyć pola w teście z grafem zależności Hilt, użyj adnotacji @BindValue
. 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(); ... }
Upraszcza to zarówno zastąpienie wiązania, jak i odwoływanie się do niego w teście umożliwiając jednoczesne korzystanie z obu tych funkcji.
@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
Ten 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ć wielowiązywanie,
możesz stosować adnotacje @BindValueIntoSet
i @BindValueIntoMap
z @BindValue
. @BindValueIntoMap
wymaga dodania adnotacji do pola
z adnotacją klucza mapy.
Przypadki szczególne
Hilt udostępnia też funkcje do obsługi 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óre 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 zapewnić, że wszystkie reguły będą ze sobą współdziałać.
Możesz połączyć te reguły 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
HiltAndroidRule
jest uruchamiany jako pierwszy. Określ zamówienie wykonania za pomocą
Atrybut 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żyć usługi launchFragmentInContainer
z
biblioteka androidx.fragment:fragment-testing
z Hilt, ponieważ opiera się ona na
działanie, które nie ma adnotacji @AndroidEntryPoint
.
Użyj
launchFragmentInHiltContainer
z interfejsu
architecture-samples
GitHub
.
Aby udostępnić komponent singleton, użyj punktu wejścia
Adnotacja @EarlyEntryPoint
zapewnia dostęp awaryjny, gdy wpis Hilt
aby komponent singleton był dostępny w
Test wierzchołkowy.
Więcej informacji o @EarlyEntryPoint
w
Dokumentacja Hilt