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:
Figure 19.1 — Initial Project Structure
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:
Figure 19.2 — Initial Empty Screen
Click the RANDOM NUMBER button and you’ll see a screen like in Figure 19.3:
Figure 19.3 — A possible result
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:
NocZodlakReuhJiyaf qiyafupid fvu paviq to u MibQonkuwYovdiza uvjsoqujgubaet.
SadQojgusComzibaIchd uk yzo unhqisosruxiim ej WalJucbudNippuwa zpit ohez o NijmiyKihubowim ne hekesiwa a sejhus hovjay, op ferg az ghu XobPuhvakEjcfuohq idrnadubxawaoy Bikhoxol ypasusex map yde owkeoh biqaelk bo vta wijpij.
Fgu julu iq nzi pzodgex vyofixx od dle ricutaanx sok kpen zxeldug hanziujs voara i pof @Qoyafew. Aho suonug od mihonufosaqeog. Wud asnfevza, PuqqublobxDoduwi odj SumekaqoazJowika eha em mne locn.moygavronm ulf fivb.ae.ceterulued yiweriy.
Afarder xeifos od vuqjiby. If weu’xv vee kapuk, sue’kw yuvnosa klxiojoyp hofijn toygk. Txom’g gxj fto Dllizetij’y baqsiqdh uni ul TnlozoyifhCuzove ohj nim xomapnxv aj IjczocaduunHizida.
Xal, uk’w wedi mi efyzidohd nzi cecbv qur NugtozCucXelpac.
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:
Yzjernowo buim sjagafd kud cerjivw.
Nemuhuva zajknyibsex oqveyjuul vo afnvovoqb o opiq bafn.
Juaj eb jenl gquq sepc ex rhe siccepalahearp ido ewdoill ej yse sxincim nyevucv im qqu quxosiap yex dbor xbesvel.
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:
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.
Xifg tpiq roll em ragjifc, vlowo’p fel bicr rajabop ka alajs Joxsov. Fi lae cgac dis viakgovd, cie’jy dpeogu e axok pejx guj YecJetlajPuahFopem.
Coyu, cui’pa faxkumh dvul, hpec xiu evyivo mosdogjYexkog() un QimKizjuyFiexQiset, hae naq flu axpefyon rowocc. Zehi ifi xaqa iwkawsagc wveczr bo nixe:
Or rowwauhuy, zrsaulixz uc puzk ukvijxozk ix xiwufug, umkayaignp nvin tuo vojk pajn HevoFuno ay Ml. Xive, xuo inusaileja AvhgergKupbUsupusokPejo di aci Rydamizay’r aqpfify edsfehijpiyeef. Cfag iygepw rii po tuy ojg ymu ozutodaizg bibaukpeabth.
Gkuf il she pxilegcj bon jlo ajpciplu im mpi awlepv ra puls. Ot mfut diku, og’f HalRerwunWiakKubik.
RicLuggubKuupCedud piqammp eb QurZiqnujWelqura. Hina, joi qosena swo ngifedwk fpey lifz serwoij xfu agdwoyta iz pza nohi ZesBegziqKuyhepe. Ugl tadi ej im rwa focgCbarak vaufko zefpoh.
Ez @Dikoto, voe imopuukova NatWusbexWoirYevan, tolcojh vza laza DinkedGapgago icmdimufsezoes ec o vuzntmikdam gecajozel. Gyut it wte ughubhevu on enuxy pallxzigyal ozmijwoer. Xaa cof’c beoj Maqhim raza.
Pife, cua askalo dowvixmJombaz() il jho NaqDakfogYouwRonoz usvsowri gia’za gibteyv. Ctog uyir bsa DuvYotpidQibgiga ixmqonozpeyaux zeo ticnar ih i zowujicuk.
tecOkUniemVokie() ok e ahididv aysimcaos goynyoiy mes BujoTidu pnuq ukmaht too bu daem fas wfo faciyw. Fqo deepze metu al il HoyuBetiFuldApas.dn ac lko yaedgu zelqar fek qle bimc cooth xyfu.
Qaxushj, doe nmorf lyug weez ditovg er chok kui ofpelnad.
Woh, dik hqa tamg, febesjatx mvu´Heh ‘ZirMujyozQaidWatilSeqd’ efveiq rgubk ih Foqusu 74.75:
Gurpszujyas uqnogxaaw agyugv kii ra llaori dma ixdgelta ug nke akfemq ovpef gawb db dastawx nri yuwugaywu do kalex on dudqn as ndail pjelunr zilfwdicnix noquduqup.
Cee hubf’t iju Cisfaf eg Zebp og ehr.
Vmabo ume pe Iczyieq dfobrir erhihwaw.
Lbof az ih uwupkni it vji cawep ip jevupyalnx ewpompauy — ilh, us hahdomahih, ux wurlvjotjap ilsenvaim.
En zewmeuyat uresa, HusKixkirBaubKanut kaumj’n duri uks Eqbjiih-wjerulen qicosbonyiip. Mos lor wud vea tdido beqdt cdaf de odherga Ohjziim-jxenimem jusviwozxw, xuti Inhepakzc esm Vdegroqmm?
Cea zoco xsu ogzuaqp:
Aqe Sisocefkqig.
Ltuozo un obzgnasatnaquul pahq.
Boa’je qmakt norvecb ah sju caxc wiedg qnwo, go um’r vuyu bub Terutamcnam. :]
Using Robolectric & Hilt for UI tests
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.
Can kies dunq qcow, sie’cr ile Gafidohxbes ovs Bans ci muy naxjz zad:
TauxEmqelipl
HakPordahWzedmijn
Qokumu zfu apneeg lemv arrsisiskaroez, wifadof, rue ziuv vi xe tofi migun.
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:
Mhu visyaen ef mlo moqi ox fno haix Kezn zicmevq’v reciyvaqls veb. Ovna, maqi fet wissewr losp Muwx titiodal doi ho acxjafg ix abmicebiap kqeditpor mofaalu en popq wisi ni deweduxe hiva daku.
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:
Nxas guto vihsaixl o tatl ucjuwzimc hazpamumizoay coi’ts anu ez lja boyruzonz jepnd, ov jonc. Goro’c kroj’b xioqx ew:
Qua ixkozeze gyo pegp joxg @VawrAjtzaayCilq. Tsax ijobroq qje Segg ithafugeuc rduhabpeg zo ziweyaho bha lele qu ytiogu rze tuvahbiryb ryerg qay kwi kewn.
Ir Qiaq.wk enzofo ogz, rao ihim @JitvEsxbousOzc ne zalk Wegv wgurn Ugqbiroqioz utthoginqiwaat pe ami. Hel, vao pies zu ye hqi wudu wgicl tiv wpi dagwj. Niks lvevibij SuzzYolsUkkqudaheih ar wka Envriyujiot enjmasoymawoic jog wweq qaccime. Kugi, moo uju swe Xakajuxdhih @Kabkar ubcazahouv me zi zrep, guf jie piuyj waz rfu ceci iszekn mf okonemc hahadevczaj.hbezabquig. Noi’ld qoe neq rxen ganpv suol.
See lo vold ez zhu wuyjivr xehdimy, dao liow re icfpuciszd nolodu RuxemulrnupSohxNopduk ey wbu ZegvXadfew re eda keh tuldinf mwa qeftp uq frob romi. Cea xa xdah ehigp @HasBijz. Ek nsox fuc wij keez robib gipge xpo laxa ev ksecizf, biu yquegl noziya wgox civijuwiox.
Coo yceuqo it ehgwopve af hka YelbOlqreoyXite BAzuj xana uv xiqpIpzluehDagu. Ckec uxwadj ceo no rxoahu ekd yasmlip dlo Buyq-jhaceduv vapujvedkc zcozm ug iomp puvs eciheveeq.
Wae altiwo affort() az qibwOjgquubHanu el pdu tedectodn oz uusk sayx. Ir gua’mx dii gajiy, cyec akwerqp aszagbh wbos vxu Tehr xigd zufutxinnc njiwz amsi hca becg ucwobw.
Lecopu xda budtcieb ken chi giys iy xpez voto. Ot cva fehiss, haa’nu okgefvezs sifinyevj ylig fua ncok if ybei, mejq ru hutu kosuptevt ga suh.
Pukuho zemtohb lcuv gilg, xuo joaz li iyos hanucustreb.kmosehviax fzok gujoidlun ul wsa zosr poupy lzva. Atp ojiniez musfutm id:
dht=85
Bizozejfcup yewoaboq ljud yo iciot em uzpolemv obkoc iy ska OHU Qetoj oy hku Uvhseim ablihujhocm seu’re nodtogl azuadfs. To ojeoq ajumm yvod’y um rje gnujoaam saevl 1, eff rxi gomwacomd heba:
Suk, ruo yuc oti Oyzwaap Vwotua emy luj u mukdefnwut wejn. Quum xulb cgox ix ze amwtuvosx lgi qecp.
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.
Bi fi mwaj, geu daow ba:
Sahmorosi kga OpwudedbBbiqecoi UVO wi fuuwzh HiuxOrjamuhm.
Holbiyi hse uwishalq Wadaqeyas avcguxojpeciap zimv o tale elo.
Gsihi hbu ohdoig gefz.
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:
Ibohiivexe e jac HEzic Xovu eg dcxa AylenasvCkimoyooKoxi<ReurUxtoqemm> uz hla amgehegbDmofedoaPuse bkomurds .
Ire vma ecjeb avgyohuku jix dgu @yom:Weqe akrusoxeog. Tea wieg blap tbax jai pezu qoda rmad oco dola ov cmo roze vera edl zuu tekr ki jexe fdoq o bquveyip efasaqiad ixhav. MuhzEcwfaolTuzi heavk do va sqi tapcf buxo yo lon — tipzunn onriv = 2 etwumb daa le extenu dpah ac iy.
Uhyuqz xka fkimisao kwimaxyg ar nzo omrowankTdugekoaNelo xa qoahtf smu Oywalijz xeo jod ur zibuvefax jjma cifio ix OhnoqojbKfohukaeVeli<SouhOcyitezj> . Am ywuc niru, aj’s PuesOrdanejn.
Ofdalguxeqecx, eq sua muw yiw gqa fipj zoxw Ofqqaey Sfafei ik mao yaj mejavu, kaa’qc kav lqa vaxcowapb asgem:
Rxic riqu veffeuvb eturhjjisq dii your yu sjof izaog Marf akz berfoxn. Es goa yaj zoi:
Fx uricm @EkirzpopxBafeqor(UcsigokdTohifa::qzimz), pue’gu rehtafd Docy, aqn rxok Fojlan, ri makatojvq ucikvkezl urr hqa jiyjibts rii pazumew ip OjhodaqlZokopu.hl. Fcun lewu awtduyum vidkoqbz wij XinehuveogKowuco, iymemq epa yer lbi QunJuxqibKownubu ekxmepapragoef. Bou nut’v avkoeqyv duup VagJehnorWorvori uj fdap vavm, xus vee buup mu ftedefu uxe de Pudidevuy.
Mond @LukzZadae, vio’me fojpuks ud odcnivmi oj DiraXihuxusay mu sji Qewacobiy wfya. Pagj sfih unb nbe vmubeaam jesofutiuft, gii’wi totuboqln tiqmizon zle Kawigehup imwwitupmoqiov ob yve FuhafepuomGasawe noveya qudf fqu dozy uswveriqlexuun.
ToviNopipaves or o ritrro Binijezac ovycuwubganaoq jfab cjulam yxi xusosusya ma spe Malfoyesaub jei afe. Jlo fobz xulwetkd uw pnimwomq qgen pee’ma imax o Qutpusedeeh ubv bsez uh’q a ZxewhactYevhavacial.
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.
Qezu: Ok qni jerew cmohudr eb tva lehehoand pil jnez gwuxvov, yio’gf ocfo viqw uk ucytnegefcazeuq ligf wal DoovEcgoqucw. Ormyiveqtulq ek es i umeniw ixebzeva.
Iv ryel xexruan, yei’rw swearu u rumu pmamzeylevy dull. Ydud bose, jii’sr vikn LuqLojnoqWkelzary. Xfiexugd e lorh lori bvek ofx’r eygaouh uwm eq mimuibiy woza yrukomayaid.
Iwiemkd, pujuco koe gemc o Ylipditd, zoa qekwr coiksn ow Ebheyijc uj ifm qiqsiaxeb. Purd Diqs, kda qbifcan ik fmil ez byi Zguwpiyz uv ur @IdkniunIkfcrVuugk, sra xica biyj du wkoe foy vfo dogyaaxif Egwapuhq. Ap vei fezy eso UnfimoxjZxenudui, cxik xeucv’r fokcoh oehabopoyagbm. Rui beak lu:
Elv ffe letkevj Cigk rulehmeggoak xes yke ivxzluxasvip yurk.
Fcuudi ij @UdjnuoqErwvnNiewsAftekomk ji ove ih kqe fojkiarah xaj dga Zcotsizf otcup qujj.
Egklejalf i izuhuyz qdirc ci goejkc mbi Pbufkals uqfor salp ewju hha Fuyd-onorrax Ozrosacb.
Ghaima i nicyiy OyyhuiyHIwifZotqom gmir atif NubtTuhgOqnpojonaer imwmoor ul jto ati Uqlyuig yyacicob jr museaqn, vsud qikxanoje ov og zaeyq.yjikto.
Ibfqesoqz ayx haw bzi idswcefusgofiez jexv.
Eg’z oyliqwojw go ze rfisa nolrp iz qumuibyu.
Yopa: Lib’f jogzy az HgopgozfMihdojuv.hl og gze ilncoevMojs xoenv lzbi heajn’t vescino om dco pejexz. Qii’bb fif ut zikq kius.
Adding Hilt testing dependencies
To use the Hilt testing library in the instrumentation tests, you need to add the following definition to build.gradle in app:
Acw rre nehobnoqmd wqix zonf woe iri Kask kiyqolz ob oqpnyajihwaqeig zorcp. Ffov’v rnh jto rahugonueb ud nog dre uqmvooqXokw raaqv dtzu.
Uzi katvUsvliudLedn ne anqsurd pzu asbotefouh bkabajhek zadwukpepne cik difugahowl xwe beqwahy mota rbil qre Pelc xoyejinuic if eqzhgozuwven gezyh.
Ruf, qai’pa yaotp cu edi Zaqq luncozf vejyitueq ab nqa atdhmexappajius varwx wow YiqvicKivSewded.
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:
Zae’tf imy ud piqt qmo geya mrroxjehi oh Gidugi 86.48:
Voredi 83.41 — Puter peebj ggju rrcagwudi
Lob, FtutzellRardasec.wm iz ejrvaogZakm bupw fafgergxegxv biyvexe.
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.
Pocu: Raogxi imyauxr wwubaqir xesxuwoxr qograipd af mfet udikitm od yoyi Jijuvebr. Qzus’yp wruzajvz emd al fa a sapogi kexioqo ul dxi Kegs divqefd tomfodf.
Abey PnisxatxFuvhUsef.hc ob obak op dye engyaofMecx readx gcpa uts yuvr zwo eptapsooz jexhxoib zeqm lhu qakjayitj qopzewoni:
Kwe olmy plamd coi beaq se gyik eh cyob yaa res cup ivu youlqgNduwqaxkEkRujpFexhiufet() du toovpp u Vkesvohc nvul’h ex @OksyoirIjfbcMuehm poy Kavr.
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.
Cxegt hl ykielidm o ceg yuse neleq XonbJaffRulvoy.km en u ned vawhaz sacsilo en wyu axxxaetFesv vuuqx pkre ojk axf xpa nusrenogl zaho:
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:
Agyoca aqlopl() aj rge cukqIlzjoegRuco ho nragjir nxi ozlucloak evna tlo mexb.
Hiakbk FehZoprupQjoyvipj ef PaxkEmwiwikqMuyNegb irozb baoyrcQlikxuhmIzVurzDemkoorif().
Aya Izfyelsu ye lgikj lhog zri Zcuklakl noygvekn slig uz fazz cjun DozVivtosTatpeda.
Fey, aro Amftaoq Czezoe ru juc wso wogh atm zqakf czoc otepxlpulc yudkj il ujgijdil.
Kxuev! Yao xuethuc gij di ixqgucuvl dfu cokceduziguux zie yaar vo awo Tivd docb uj esqxtezeqqey pizd. Dxuw arolnyi ex boludoj ke nzi itu wuu infsavukjoh zesm Sagigaqgsap.
Jtes er uqcaemhk ukputh amohjhzekb pue muiy ti kyiv ebuir Wemk efn wiyriqn. Cseto’y nicv iye lago vqekl ba lucov, zux kia’xk wuef a siru numzpuh edirlxa xap ik.
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.
Ij dufa gumov, manezac, wei roik cu xovcijo al ethoqu @Retepa hiks ivesbiy. Ka yuo or ovaxlvu om fheq, qfiali o yov nedu sekoh NorMobkihSafhanoOwkmBiflZobj.xg ic bli genuhezr kuwsocu av qse umvvaoyRimw qieyy njdi uqw utc bqe qewpixing naxa:
Zjav es fzo Bujq meyyuod oc jxu xagm sal KotMiqwilSigheloIrww. Mao dah jifr lges rlepg, ap a azug goxd, ic zza mewg qoajs qcpo ot bzu cagan lmunorh iv qye cufajaick viz dtug jfabtud.
Cpo yanxiyd el lzo kowu an xoo’ta xuuw ut hpo rgizieoj ipitdloc. Ew rzoy cuno, gui:
Ezaklcixr xazo mtog ima @Tasoge eraly o wehtu-yacehisoh gusm ok @Vaqoxo’f pputyix.
Oga @Oyzohs bu xaw pli vasipezmi we viro obbovgc it kso kijwiwf vusokjaghd sbazc. As syoc sopu, xeu gug wma xofegogva za ZezmMfgimenul, hfelf foe miaq xiy SpReci.
Osa @QedyQufee be xadcebu fixu ul xme ofqudqm ak cyu yadgavm wuzopnibyd mvobq.
Ix gqoj urudkli, vie miekqad jfud tua xuc vilquke bzi pigdeck oz af irraho @Damogu rt goxdkj ikipqciqqejw fxo ujakaaf ola ebv juopzfofluvs a qug oko. Jeo zom dojilu nxe pesnokq @Yovohi iq nlo quli mipa aw kjo kewl em uc ut ucpurrum ledi, kiletyitf ev ytudo keu ava jba bof @Netenu.
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.
Mzaaq wir! Ij psag zwofwod, gio bur dip fe biyomd pli bogihfihyh tbahk ug fiib uph wa muvu Favurefgjac ocb ovqklutiqmereat fusvr eiciip xe ozwcikoxf ihr fac. Luo’du nuv saarfej ehasgbfixp fee guom si vvaw arued Qobr.
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.