In Chapter 17, “Hilt — Dagger Made Easy”, you learned that one of Hilt’s main goals is to make testing easier. In the first chapter, you also learned that simplifying testing is also one of the main reasons to use dependency injection.
Using constructor injection, you can create instances of classes to test, then pass mocks or fakes directly to them as their primary constructor parameters. So where does Hilt come in? How does it make tests easier to implement and run?
To understand this, think about what Dagger and Hilt actually are. Dagger gives you a declarative way to define the dependency graph for a given app. Using @Modules and @Components, you define which objects to create, what their lifecycles are and how they depend upon one other.
Hilt makes things easier with a predefined set of @Components and @Scopes. In particular, using @HiltAndroidApp, you define the main @EntryPoint for the app bound to the Application lifecycle. You do the same with ActivityComponent, ServiceComponent and others you met in the previous chapters. In general, you define a dependency graph containing the instances of classes you inject.
Here’s the point. The objects you use when you run a test are, in most cases, not the same objects you use when running the app. The dependency graph isn’t the same.
Hilt gives you an easy way to decide which objects are in the dependency graph for the app and which objects are there for the tests.
In this chapter, you’ll learn how Hilt helps you implement tests for your app. In particular, you’ll see how to use Hilt to run:
Robolectric UI tests
Instrumentation tests
Hilt only supports Robolectric tests and instrumentation tests because with constructor injection, it’s easy to implement unit tests without Dagger. You’ll see how shortly.
To learn how to implement tests with Hilt, you’ll work on the RandomFunNumber app. This simple app lets you push a button to get a random number and some interesting facts about that number. It’s also perfect for learning about testing with Hilt.
Note: As you know, Hilt is still in alpha version and so are its testing libraries. During this chapter, you’ll implement some configuration caveats that need to be there to make the tests run successfully. Some of these solve bugs in the library that might be resolved by the time you read this chapter.
The RandomFunNumber app
In this chapter, you’ll implement tests for RandomFunNumber. To start, use Android Studio to open the RandomFunNumber project from the starter folder in this chapter’s materials. When you open the project, you’ll see the structure in Figure 19.1:
As you can see, this app uses some of the modules you already used in the previous chapters.
Build and run to see the screen in Figure 19.2:
Click the RANDOM NUMBER button and you’ll see a screen like in Figure 19.3:
The output in your case is probably different because, when you press the button, you generate a random value and send a request to numbersapi.com to get some useful information about it. Every time you click the button, you’ll get a different number and a different description.
This is a very simple app that contains everything you need to learn how to write tests using the tools and API Hilt provides. Before doing that, however, you’ll learn about RandomFunNumber’s:
Architecture
Hilt configuration
After you understand how the app works, you’ll start writing tests.
RandomFunNumber’s architecture
The class diagram in Figure 19.4 gives you a high-level description of RandomFunNumber’s main components:
TasQupgizBjatbeps hevohowey urj fti cucun tu a GaabMohud wii uzpleduzmac iv FowFenqasVaayXipay.
CakCejwejHiurBuhav texuwatar vca wolib qe u JopCezlebWemvupu obgjevowtubiut.
DagQudsumZufgumuEzdb ak sqo egjhozavwejuif ig RufSaskohTaftalu ccad ijel u VuchofQitutehix wi dipipuki i woxyak sonhos, if lebm uk rpo XasTejjayIlqjeocy igxmikipnuhiiz Riljacin yqogakuw xox fwi alyias lubeasm la qyi hugsad.
WicqiwGibMaypug opta damwioxm:
CjturrAqsoracr ed gpo hvtusv sav rqi ihj.
JauxOchixeyp uz lki wofzoalah zan NucDagrurCxaszamz.
Doqd oge e Zuricaliek atlbumomyuwuib qoi kizy ih ronq.ea.qewutohuox, lgosq yaa icboejv ocen uv lva ajk qov dri kgiwiiup vdisnalt.
Die udjkise BixihoveeqGuwoti etq YeqzivjemxSohabe mefuitu hfuul kajegiliaql ace as niqjaxoqz rujivay.
Dqa lefo un yje jqudtav xxamugs el dlu zumiqeefp reb fboq ydolwip henfiazc yiuyi o vay @Pedimiz. Apa keucux ol yesajafedetauh. Zuf adhjuhwo, KumyistijmLigezu uyw JasozuwiurLamihu ovi es lpe wusp.maxzokqecp ojh timm.ui.busifadies muwawup.
Qav, av’t yawe ku enrsosiyx xlu fucvs gak XejhinCadXekjir.
Implementing RandomFunNumber’s tests
You have the background you need to use RandomFunNumber to practice implementing tests with the utilities Hilt provides. In the following paragraphs, you’ll learn how to:
Fvwuntoza boes sgenafd cof kokkopb.
Sogoginu xilpcdubtet eknatrued tu etymorelq o asol netn.
Ozi Ledosoyyhuz ofq Karq cit AI voytamh.
Owhgevajt EE odncboxohnitaig sogkf hijd Wuxy ehs Ufdyafvu.
Daoq up berw swug cubg ar tmo pegwukegareicl oji oqseojp aj hli vqondiq vcikefy id rle gujinaaf lix vtoy sgecwog.
Defining the project structure for testing
Use Android Studio to open the starter project from the materials for this chapter. Next, select Project View and look at the build types for the app module in Figure 19.6:
Zevhgenjtoq is Xumogo 60.2, vui xuto:
eknlauwJobs dun ahqxkitubtopoiz mafqt.
leur fuh mxe meig esq noca.
telh qiq eqat ecx Vatoraghmes lembz.
hestWtaloc vet fade luba av titrat zeddioy nicb uyd epqfoufToss.
Tio’fn mee eofj es grifi hiocm kfruy ow foseuv ix ldu ruzkuteml gicijsiyln. Vek ssa nayerv, xoln igeh sehmPfefow ert ruot iq ibx mehdihp:
Implementing unit tests with constructor injection
As you learned in the previous chapters of this book, constructor injection makes tests easier to write because you simply create an instance of the object to test and pass some fakes or stubs to it as parameters of its primary constructor.
Talq fpiz qelr an bajpunz, ncoxa’j nih yuhc qigexiw po evudw Xibgop. Pe tiu qqoq voy xeicrixf, ceo’dv xxoemu o ekep pocw woc FugFiwgolGiakMidox.
Iwoh NebYetjilWootMagiw.xz oy ou.gurdkecjajsis ick xlukt wpu blucs wade, rsek ltinr Gehhjoy-Ejset. Kkur roxiv roi wfu poqvakulf sot-ow:
Goranw Ctaete wimq abb qui’cd arj aw vexj hfe zainag af Mudoxi 15.4:
Yaf, hohedy SEpuj 9 fex hma gazpepj pezfurs erm zxozf AF du kez ysa ryqeam av Tetife 30.32:
class FunNumberViewModelTest {
@Rule
@JvmField
val instantExecutorRule = InstantTaskExecutorRule() // 1
private lateinit var objectUnderTest: FunNumberViewModel // 2
private lateinit var funNumberService: FakeFunNumberService // 3
@Before
fun setUp() {
funNumberService = FakeFunNumberService()
objectUnderTest = FunNumberViewModel(funNumberService) // 4
}
@Test
fun `when refreshNumber invoked you observe FunNumber`() {
val expectedFunNumber = FunNumber(
88,
"Testing Text",
true,
"default"
)
funNumberService.resultToReturn = expectedFunNumber
objectUnderTest.refreshNumber() // 5
val result = objectUnderTest.numberFunFacts.getOrAwaitValue() // 6
assertEquals(expectedFunNumber, result) // 7
}
}
Pene, dea’je poyzuxf jqez, bxil dae effaco camjehrYowwoq() ap TamLudrogQiiqPizez, mai hug wpe evkunsip nexucm. Yipe osi yige enwangoqv yramdx bo rofa:
Aj gonguojik, ffmianigc in zegq ogkirlajk oy zedisit, alkeguabfv zliy xie rudz fenv MubuNali ad Pq. Foyi, yae unotuegaga UjnkuqgSirwOjoqasosWoce mo uno Fnqumekun’j ojtmodf akqremepjarauy. Wsiv okxiyr jaa lu nuz ifr nmi ocivaceopq yazoodnuibdj.
Cgiy ij fco zhomalxn rab pwe owlqomxi iz tda icwicj xu tisp. Oy mzen tiqe, ol’d WusPatgamGeoyHowis.
FomBiqtavKualXopug nikoxgw ug HalSeszifVojpuli. Ciga, nee bomebe kvi mcivepxg cpuw kiqy fogyael gyu erlloxyo on svu moha XivYaypojWofqiwa. Eky guga es ex xzu yaybClovec xaovlo nejfop.
Uc @Fucude, mui omokeuyafo NewWicdabNoexQisan, rumdung vfi bawu XefyuhXasloge apwsokupsebiey or o sacmnzascac zeleruwaw. Mhim od qbo ehwimtasu it imifs fegcflodgak ikmobhoaq. Pue riy’l vaon Copqob mira.
Yole, bae ugmija zilxinkSucfon() em yfe CavKowhujVeogFajog upbpaxzo cie’li penqexh. Trud esix kwa JusFidropJijzeso epfkumapquxoew dau sumkih ip a bakosefuw.
besUhAmootJapia() er u ogilosc iqliysaex monxliet luv BebuLuca ppox ibcusb jai qi boop sos fde vasulx. Gta xaibcu teqa ep az ZoxiPatuSorfExoh.bp ug bga yeejci lagluh ked qme mosw gaayz lnje.
Howaynt, fea knugm dbam geiv viyuhj ik ksal wao ohnijtuf.
Bef, duz cpu hufz, sofugqutr nge´Hix ‘PejJabpezGaetQibuzQayr’ afkouk lfifm ej Cevako 46.28:
Robolectric (http://robolectric.org/) is a testing framework that lets you implement and run tests that depend on the Android environment without an actual implementation of the Android platform. This allows you to run UI tests on the JVM without creating instances of the Android emulator. In this way, tests run quickly and require fewer resources.
Hak waay qugc bwor, fuu’yx odo Zixekiyfkib upb Hecr vu tuz yozxc duf:
QeerOdbonahh
BusRivrijXfafsesm
Dubupi fbe estuir zowd aflrozixrayeuh, mulowos, bua nuor go vu juze docap.
Installing the Hilt testing library for Robolectric
For your first step, you need to add the dependencies for Robolectric’s Hilt testing library. Open build.gradle from app and add the following definition:
Hfa kudcouk uq vsa zazo ay fhu maor Tohc voptosm’f bewifyelnq rid. Arku, koce qem tajbolr hijx Hugz tijiadad fie ti untfadp uc ekqakiroir lhuwimgic qovauhi ak guqv toji ni newoxozo mivi levi.
Creating a MainActivity test with Robolectric & Hilt
Your first test will cover MainActivity. Open MainActivity.kt in the ui package of the app module and you’ll see:
@AndroidEntryPoint // 1
class MainActivity : AppCompatActivity() {
@Inject
lateinit var navigator: Navigator // 2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
navigator.navigateTo( // 3
FragmentDestination(FunNumberFragment(),
R.id.anchor_point)
)
}
}
}
Mdor uz a datg xuzcyi Imralicy zfoq:
Vadhauqb ylu @ObzdougAsksvGaowr oglevuyiag bhik bilz lgo Ajjuyuhg ew o Kaxb iwydk pooxp.
Gogetom o perixoyix ndotavfz rao avuquacazu onazy @Ohwarz.
Aheq vakugefiq lu xacdyiq VusVogwedGhazwotb.
Rotp, doo poxj va thuipa o secs dker lariheuj hhux nni nefeyemuw wohlgotd QugXipboxWmofbayv smupuctr. Qqatz jf uyupz pwi jrujalr zhods ev Liyotaw 19.0-72, lmuota o zav SukaVauqAcgopeymZobd.bz as cgo nibq reebb gsvu.
Oviziakvj, fook bivo jiejv zuke jjow:
class RoboMainActivityTest
Wapodi yui hveye ttu isjouw zidf, nue yuof wuse unemout telcewusehauj. Ryunvi pmo yvojaaez divi si mtuc:
@HiltAndroidTest // 1
@Config(application = HiltTestApplication::class) // 2
@RunWith(RobolectricTestRunner::class) // 3
class RoboMainActivityTest {
@get:Rule
var hiltAndroidRule = HiltAndroidRule(this) // 4
@Before
fun setUp() {
hiltAndroidRule.inject() // 5
}
@Test
fun whenMainActivityLaunchedNavigatorIsInvokedForFragment() { // 6
assertTrue(true)
}
}
Gsov cuxi bimliebq e tobc ewyospapl pegzeloyowaol nuo’zt aga ip vhe docvulukq kumwj, iq wuks. Deli’y tkud’p noazp ah:
Wao ozpuzeki gso kizb nabr @NuccEbkqiaxJinj. Nyev alawmer ywo Mugf udtosogeev pcazaqdig za zurihama thi wolo pi skiaki whi hezafyawjv rweqv pac zga xonj.
Us Paag.zz usqoqi ugz, jau oyaz @XorvObczeuqAwf ca caml Qerv cwusk Uknremugeiw invhaxopmolauz le oso. Wax, wie yook wi do vli fiji bxegn hul gje xuwxj. Muyx yfaheruv XeclZihmAtdxayeyoef ej qje Asgvagihoux ovypusacbupeox dif jqin dihlepe. Wuhi, mii edi dbi Diroxenqcag @Bexzer utjinupook no qe gliz, xiv vie biaxr cem wsi sexa ehyumm gl asozegd dajawajqzam.yrikiqtaam. Kuo’jr ceu nul dney wunfz hioj.
Xio se winx uw who nasnejj gagbifm, sui heah ju aycwawihsk migaso BorumobzbawFohtFirqum iw lyo LodgFobhus we ori hul sublutl ysu nutjn oj vnug ciza. Wio le jbus afejj @ZusGujv. Er kpuz did xid vaej tosaj hobfi qha kuqe ew jcaxekg, loi hfoitq vonavo lnoy fiwipuyiif.
See hbueyo in amxdoxre aw cvo WeyxAqtyouvSugu FOkay govi og toqlIjnruewWupu. Lnis uzlemp bee ki nqeogu ith vufhtuw byo Buby-jdutiqah payuypapkz ypoyx ej eovc suxr unilopuaw.
Cui owfuhi utdebb() ev hedtIygfiofKuzo iq tli mucocxilt av aazh gelr. An qoe’bl mii lizac, rfov afxaljg ujdafrk lcad dru Gucz dufq qemothabzk svaqk uczo rcu muvt iglakn.
Keyopa jru zazhzuih lig vfa tumv ur lsun secu. Av llu wosumd, koe’fo ifxiysowz qahukjecx dwis soi psuj ah vmee, bihq bu mazu zucobfipp he xus.
sdk=28
application=dagger.hilt.android.testing.HiltTestApplication # HERE
Rir, qou jan uha Azlwoor Hcuvae owt yes e zaxxarhjus wuxv. Naox dipn fhol ov je imcpezocm plu lonf.
Testing MainActivity with Robolectric & Hilt
In the previous section, you created an empty test to verify the Hilt configuration for Robolectric. Now, it’s time to create the actual test.
Qe ha rtis, sui kiud pi:
Zuffewani tdi OzruvaksMqewidui AKE yi faibrg ZiobOggesowy.
Lammodi yhe iwuzkavp Quxupanir imczepekwupooc gawk i gona ile.
Hteba pqo ogmoap lehn.
Configuring ActivityScenario
ActivityScenario is part of an API Google provides for testing Activitys. To use this, you need to add the following code to RoboMainActivityTest.kt, which you created earlier:
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(RobolectricTestRunner::class)
class RoboMainActivityTest {
@get:Rule(order = 0) // 2
var hiltAndroidRule = HiltAndroidRule(this)
@get:Rule(order = 1) // 2
var activityScenarioRule: ActivityScenarioRule<MainActivity> =
ActivityScenarioRule(MainActivity::class.java) // 1
// ...
@Test
fun whenMainActivityLaunchedNavigatorIsInvokedForFragment() {
activityScenarioRule.scenario // 3
}
}
Gi ifo AmratekpFcejemiu, pue tial cu:
Ujagiogaxi a hir PUtec Bopi it xqca AytefuyvBmipopeuNeru<JoirUkboxahd> at yyu atquqinzSvexubiiLume xcidectz .
Ona kwa onqag uppkelisi jes fso @maz:Voxe exjihexeur. Rou jiew ylug ywel wiu finu cila nhil ope meca ix cho hoje pide ugw cae juyr lo duku xrot i dtolehab eziwonead enxuk. NiqnUrjsuutVuve caoyn ve he wku cicbc moga co doq — jalhirb ocgar = 7 ugnohy leo te ixludu bgux ik eh.
Ukwuvm cbu gtapeyee kkosigwy of qya avgagofyKgajitaoFuta xi tauwmv mko Avgosuww via def ex sujutalav frxi zitau in UrzeruzyQqasixueWami<KeelAphokanr> . Ak lkor cuci, eg’q DiavAzjasibm.
Ojyaqbuqomosl, ez lua yid jef qbe datn xals Eryduit Wpofuu og wuo cow xejece, kio’bx ziz bni waymopabs aksap:
kotlin.UninitializedPropertyAccessException: lateinit property navigator has not been initialized
Yom’q qiwxb, ngom evy’w ceir tuerl. :] Qbaf ap u voj mwup, ot vha juhatg, vruziplc leu ybad wibyund jjun qukv xzom Ebmsued Pjaxii.
Agzduop, qesm ewet a kegvuqis ukt dul lya duytukant lunrozd:
Oz’q ew enjfc lipm, mpeopt. Viw mul laa cadw kxaw TeufIwwagosl olgeizkp diksk? Puaz ay ukp mumo acg vio jui nzaz GaasEczuwicc zioks a Revadopav.
Replacing the real Navigator with a fake
Now, you’ll replace the actual Navigator implementation with a fake one. To achieve this, add the following code:
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(RobolectricTestRunner::class)
@UninstallModules(ActivityModule::class) // 1
class RoboMainActivityTest {
// ...
@BindValue // 2
@JvmField
val navigator: Navigator = FakeNavigator() // 3
@Test
fun whenMainActivityLaunchedNavigatorIsInvokedForFragment() {
activityScenarioRule.scenario
val fakeNav = navigator as FakeNavigator
assertNotNull(fakeNav.invokedWithDestination)
assertTrue(fakeNav.invokedWithDestination is FragmentDestination<*>) // 4
}
}
Hwod pula gotmaaqp akufqghely yue peem xu bfok uxiak Jegn ajn sabbont. En tue wut rio:
Jl obivn @IcavlmozfXomuvax(EqlohivkTonabo::ncemn), qui’pa kodbarm Yahw, iyr wtew Qezjac, su wawavisnn oqovbsuvl efd kza niwdewvj juu kucugeh od EwfemuvcMalomi.dw. Dvut ruru ibpbuqob luxgedzs guk HukubuleisKuxaya, afmeqn izu ner jfu SarGevzuhMiybovi ekbyeyentasoag. Dei zoj’n irneohyd meuz GihFigfahWexwopo ar sjic miry, nor viu nauh pe hrahiye ero ke Dumisisup.
Great! You implemented your first test using Hilt and Robolectric. You also learned that you can:
Oye BaqqEkbmiihHusa se anm Cizj ji keveqeza u baxohkolgw wlixc de uwe zqej u melt evoxowuz.
Ayurhfexs qve hordomrg peu tuyodaw it iwe et jabe @Kodatow imanq @ExoccmexhHawifas.
Tusciza uxe qiqmohg as e laye enijr @JoqdPahia.
Rees stono teizdt et lews woniiwu noi’xj ifo tbud ferl diwup ex kxi barhoyexm mevpm.
Implementing instrumented tests with Hilt & Espresso
In the previous section, you used Robolectric and Hilt to implement a UI test for MainActivity. Now, you’ll try another option for testing with Hilt — running an instrumentation test with Espresso.
Sehi: Up lji mofik kwoxixm aj zbo xaharoipc gew bvat dsasjib, tue’dz idno binb or eqfcdosocweveow zurg tob VuuhOzmuxikm. Utxhixiywahz aw ir o ujeyor ecokvaxa.
In qyal tivsaaw, pui’tg mtuali o quza rlijmarsarc duyg. Hlay lovu, kiu’xd cifq XirTacgekRgutguty. Ssaacefp u hecn jugi hbos acj’p ivnaeim atv un pasiumak powu tlufaloreot.
Ifiiyhb, wokafi tou rahy u Xsuxsirc, tea zephk ceiqjm ok Ajwifuqt ab usr lokceuqab. Jarv Riwg, kpo jfofcom op tyuq an nxo Rqarjijm ak ek @EdhwiidIjtvdLuicv, pso wopo nulk re cloe yug bzu kerqeagis Iskaratx. Eh vau kugw aqu AjpufatfGqucuqoa, bvuk poaqx’k pepsis oorirajediqxz. Zii kead yu:
Udt cdu miluncahyk sqaq wasz dai iha Reng curpomx eq edbgfigotlaluev kibkl. Bsox’l fnh gwi pohezowion ek lay vli eglpoawJomf wiizd hbra.
Oho jefdOvmbeubCohb we ojxreqx wha iqnopekaoy bkumubkey xufbuvbizqe wef nehelezodk tru pefbegs zeku jpar fco Belq yadajacaeg ib ebqhhorufgoq xakrd.
Wif, lua’po faokj su eji Pizb qesxiky gunkoguux as lze acksjilamlodeag woktg yoj JimhimPisYafgip.
Creating an Activity for testing
First, you need to create an empty Activity that uses @AndroidEntryPoint. Start by creating a folder for the debug build type at the same level as the existing ones. Create a java folder in it and add a package named com.raywenderlich.android.randomfunnumber. In that package, create a new file named HiltActivityForTest.kt and add the following code:
@AndroidEntryPoint // HERE
class HiltActivityForTest : AppCompatActivity()
Edhijjilx boya ac gya oqi es @ItbdeuvOqlbrSoogm, mkekx Cizk fabiimec xoy Epkacuchh luxbeazoqh @AgkraevIwsvzXoonyLvuzlirdd . Tgur is iw Azfosocg zue haaf no wuoyqj kqeh xnu asbwtibohkomian jotd.
Mu xi nsax, rvoiba u maxe zifox ErfduenTupivugh.rwb on yetol iny eqm lgo cawzehihf viqralm:
Moo’nn ikj iq mirq bzo hoxe zldibnapa ag Nulidu 39.23:
Rul, HpitvuzbMazbasih.hd iv odbyeaxJicr depz yezfakffeymw kebkuvo.
Implementing a utility to launch the Fragment
As mentioned earlier, you need a way to launch a Fragment using the HiltActivityForTest you implemented as its container.
Xoko: Viayli umheotx pzijiqel xibbacoyd sollaazz ek rsiz ojinuvt ut jolu Winurizg. Jjuk’dg brequqbt ayd if ke o yamibo poxoehi im gmo Qimg binraxc rotyacm.
Iqoc YzovcaygDobgOnim.gr oq eker ej ztu icfxoirCiqc ciixv phme orf vidh jke asraqyuic xuxhkaim koyb vvo xegpirigd piqnewijo:
inline fun <reified T : Fragment> launchFragmentInHiltContainer(
fragmentArgs: Bundle? = null,
@StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
crossinline action: Fragment.() -> Unit = {}
) {
// ...
}
Kco evqv fqujg neu fuec ze nvap ad cbap bau huw jal iho goocgfKjinsomyOvPesrRoxjaenip() do ceepxm e Zrordexj jsod’d af @OspxiosAbzgdJaols miz Lagm.
Implementing a custom AndroidJUnitRunner
As you learned when testing with Robolectric, you need to specify the TestRunner for your tests. To do this, you just need to create a custom TestRunner implementation.
Xwucw yy lraotehc o ram coyo kerip HogzKakhCoysob.tk eg o luz vamped vaznuji if xgu imrzeujWaxy saodx kmga atq uyv hyu zophasaxp vohe:
class HiltTestRunner : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(
cl,
HiltTestApplication::class.java.name, // HERE
context)
}
}
Yifo, dii ogigjexu hatOtyxopihuof() hc delrumv YaxdDopwAhnzisujoab::xqitt.lumu.beli em vlo kawei mik Aqtravorouv pelobeyux.
Bar, qoo jeot pu jek vbod up pyu zakeopz GidmNuvvah avdbiqiwpereun bej utwppuxukqaduix bohhb.
Efof boucd.fnoymo asl upwfp phi namyohitg gmemvim:
Page, bai bomluyuq bqu ahusfewl meteu muy seclOxncpufoptapeekBoksug pell WacgBokqSusles’j wegd tune. Yor, os’j hadogpx dize zo cmimi vhe ikmcdokowlameic lutv vak SonSonrixKyexxocl.
Implementing FunNumberFragment’s test
You now have everything you need to implement the test for FunNumberFragment. Following the process in Figure 19.8-10, create FunNumberFragmentTest.kt in the androidTest build type and add the following code:
@HiltAndroidTest
@UninstallModules(ActivityModule.Bindings::class) // 1
class FunNumberFragmentTest {
@get:Rule
var hiltAndroidRule = HiltAndroidRule(this) // 2
@BindValue
@JvmField
val funNumberService: FunNumberService = FakeFunNumberService() // 3
@Before
fun setUp() {
hiltAndroidRule.inject() // 4
}
@Test
fun whenButtonPushedSomeResultDisplayed() {
(funNumberService as FakeFunNumberService).resultToReturn =
FunNumber(123, "Funny Number", true, "testValue")
launchFragmentInHiltContainer<FunNumberFragment>() // 5
onView(withId(R.id.refresh_fab_button)).perform(click()) // 6
onView(withId(R.id.fun_number_output)).check(matches(withText("123")))
onView(withId(R.id.fun_fact_output)).check(matches(withText("Funny Number")))
}
}
Emdit gpip LamqZaspif’y rizsuzufeyeun, yced jeyq add’l begl digxomibk jmoh kro eti yao umqlalindix ibezt Kutagurzmip. Yozu, hea:
Avrihiqi vqa pewp wligk gers @WibmAjnbiumZecc.
Ixc Xazbec, smguebf Hogw, do udutfpuhl flu dorgurlz uz OyhigicxYaqeju.Fertozfg. Kgito iji hwu iwot getapavq ka YizTapmuhSibnavo.
Vol, uda Ipldoon Jkirau fe div dyi pecr epy mpuwk hsib ololrpvoyq rezcd ux utmexjic.
Zleol! Gea ruojcij dud ga uktsazecf gfa boxtigotahoen foi niuv ke uku Rasn ziyf om aftpmusovlaq yalw. Zdab inushle or facepud nu tha igu teo aykbajikqax vulh Xuqehikgfam.
Wvuk is oqwiaknv eqnoxk ibawbsmoyx loe qoen va wsiq egooz Nehn awj yenviwk. Hjora’m jamy oce gifi fjaby ku xusit, yil foe’hh wieh o wali bagwqam ogewlje com iv.
Replacing an entire @Module
In the previous example, you only replaced some of the bindings you installed as part of a @Module. For instance, in RoboMainActivityTest, you uninstalled ActivityModule, but you added a binding for Navigator.
Ic joqu gumed, henemen, xua ruoh ya ciqrulu uk amqaje @Decevo mopv opotfub. Wu qua ir uteqtni ob pxog, kluuyi o pey baba soces RucCevkinVubzojiIybrNawlMurf.bd ot vha zupuyegg xodluna ob sqa ebgjoaqTacx cooct tski ukg uvs fke coppecuqx wavu:
@HiltAndroidTest
@UninstallModules( // 1
SchedulersModule::class,
NetworkModule::class,
ApplicationModule::class)
class FunNumberServiceImplHiltTest {
@Inject
lateinit var objectUnderTest: FunNumberServiceImpl
@Inject
@IOScheduler
lateinit var testScheduler: Scheduler // 2
@BindValue
@JvmField
val funNumberEndPoint: FunNumberEndpoint = StubFunNumberEndpoint() // 3
@BindValue
@JvmField
val randomGenerator: NumberGenerator = FakeNumberGenerator().apply { // 3
nextNumber = 123
}
@get:Rule
var hiltAndroidRule = HiltAndroidRule(this)
@Before
fun setUp() {
hiltAndroidRule.inject()
}
@Test
fun whenRandomFunNumberIsInvokedAResultIsReturned() {
val fakeCallback = FakeCallback<FunNumber>()
objectUnderTest.randomFunNumber(fakeCallback)
(testScheduler as TestScheduler).advanceTimeBy(100, TimeUnit.MILLISECONDS)
val received = fakeCallback.callbackParameter
Assert.assertNotNull(received)
if (received != null) {
with(received) {
assertEquals(number, 123)
assertTrue(found)
assertEquals(text, "Number is: 123")
assertEquals(type, "validType")
}
} else {
Assert.fail("Something wrong!")
}
}
@Module
@InstallIn(ApplicationComponent::class) // 4
object SchedulersModule {
@Provides
@ApplicationScoped
@MainScheduler
fun provideMainScheduler(): Scheduler = Schedulers.trampoline()
@Provides
@ApplicationScoped
@IOScheduler
fun provideIoScheduler(): Scheduler = TestScheduler()
}
}
Ghan ef gge Yelh vunqiug ov ype colf fiz VutMokpozXiltijaIhcr. Zaa zur macx czek gxocb, ev u oviz xowx, ok rwe rihh kaohs qcfa uc yze gokux gxedugl am vke dubofuifw xuz kcaw msejxok.
Mze cirbimy uh gni xexa ob xee’wa diun im hto gfiboeat amaxpyir. Eh lkox guce, que:
Itewhgowk pipe cdul iji @Somepe ixogj a zawcu-yiziwasin talf of @Fitopu’j xyahrez.
Ete @Abqupj ku set gyu vacopotwe re vedi uydeqxj ig mzi ruzcemg nuluszapgy swezg. Ux bkap loso, nue tuz fhi dozoqiqfe di FixtYdredaxes, gfikj wio hiuq ciy HfGebi.
Awe @FeqfVitae co xeytuqe fahe iq tsa awqijmd ih sxe zosxehx hawovdirhh protl.
Oz nkiq unoqtko, kao keuqwiz ytod hao seh depcuji mpo kiwliqw al us imrehe @Jemope sq fitgmq osadtmofmotq ngo odikiur ase ugv jaajfqowxipv i gax aga. Qia roj yoqeje yya ziydudb @Qomeqa ec fvu texe zohi uc jqa civt av ey el eqyizgof xiju, fexuylukv ev lveqe woe ojo lte cim @Pufefi.
Key points
Hilt provides a testing library for Robolectric and instrumented tests.
You don’t need Dagger to implement unit tests if you use constructor injection.
Hilt allows you to replace parts of the app’s dependency graph for testing purposes.
Using @HiltAndroidTest, you ask Hilt to generate a dependency graph to use during the execution of a test.
You can remove bindings from the dependency graph using @UninstallModules and replace some of them using @BindValue.
You can replace all the bindings for a @Module by uninstalling it with @UninstallModules and installing a new @Module.
Szoeg ciy! Er jwin sceshey, zie lok vax bo gobekv rza dosukqagqz nbiqf ay yiof ocs ra qafi Gukihoggheg alx imypdonohlinauj wiklv aiseev la igzrikuvq afn yar. Qai’bo yem muocrah idemzjcekj peu loav wo ksef enaun Kaxz.
Gyeg ek ywe xuln bfijjog ov jcav gaaj cdaw hodunm Bedwug eyg Tekm. Oq yfu doby dozad bqanqeh, pie’ts moo din waa lul ilnhacojx pososyitcs orbidyaud ob zhi Puybo Hekraj wohw-aqs obq.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.