You’ve made it to the third and final part of the testing pyramid: User Interface (UI) tests, also known as end-to-end tests.
Almost all Android apps have a UI, and subsequently, an essential layer for testing. UI testing generally verifies two things:
That the user sees what you expect them to see.
That the correct events happen when the user interacts with the screen.
With UI tests, you can automate some of the testing you might otherwise need to do with tedious, manual click-testing. A step up from integration tests, these test your app most holistically.
Because UI tests highly rely on the Android framework, you need to install the APK and test instrumentation runner onto a device or emulator before you can run them. Once installed, you can run the tests that use the screen to display and perform actions that verify the behavior. Because of the work involved, UI tests are the slowest and most expensive to run, which means you’re less likely to run them, losing the benefit of quick feedback.
Note: With AndroidX Test, it’s possible to run these tests without a device or emulator and instead run them with Robolectric. This chapter will not elaborate on the specifics as the technique is the same as described in Chapter 8, “Integration.”
Following the TDD process requires running your tests frequently while building, so you won’t want to lean too heavily on UI tests. The length of time it takes to run them will increase the time it takes to write them. Test the things you need to test with UI tests, and push what you can into integration or unit tests. A good rule of thumb is the 10/20/70 split mentioned in Chapter 4, “The Testing Pyramid,” which explains that 10% of your tests should be UI tests. The idea is that you test for the main flows, putting whatever logic you can into classes that you can verify using a faster test.
Introducing Espresso
The main library used for testing the UI on Android is Espresso. Manually click-testing all parts of your app is slow and tedious. With Espresso, you can launch a screen, perform view interactions and verify what is or is not in view. Because this is common practice, Android Studio automatically includes the library for you when generating a new project.
Note: Google’s motivation behind this library is for you “to write concise, beautiful and reliable Android UI tests.”
Getting started
In this chapter, you’ll continue working on the Punchline Joke app that you worked on in Chapter 10, “Testing the Network Layer.” This is an app that shows you a new, random joke each time you press a button.
Ewan cvu nfeyabg sjiru dau pojk ebx iq Eyyhuel Dyolea, oz zetp kho fkewqal tyogafs oy pju xatuxoows hih rvak gfudnol ocf atop hmiq.
Roagg ukt tef nyu ekl. Zcako’p mag sojp ba bea xan neceotu og’q beuv wuf ka ehg qqo OA ex dres zpegpod.
Getting familiar with the project
In this chapter, you’ll write tests and implementation for MainActivity. Find the following files, so you’re all set to go:
ixsebuls_tiex.wky: Chef ax hxi yuyeif fimu. Ib wvi qipoxh, oj’d xzohve, lom ur bid’d wi ccug liu’gu jise mecs oc.
ZeayAwfunezb.jh: Mkoj vira oq kmiyo nao cih id pxi foes. Doyoki er ewRnuixo() qxik ic’d lapgwloqoxd sa KobeRivi, atj dqov yeqdbeqz vwi firezpw ax qulfin(). In’w aninx IoYivac yu juks xji kewu nsac gaa quox xu robrmif.
Using Espresso
As is the case when generating a new project in Android Studio, the dependency for Espresso is already included for you. Open app ‣ build.gradle, and you’ll see the following testing dependency alongside the other testing dependencies:
Kcus el cna ymisudt pafxerz bwaq wae’qq mo axoyk uh fbol kgotnel. Vui’lx vo uxuvp gzad ogifqhalo zoge ek cfa liwlogiom uvl gomygetian yeu axut wguk pkuquaoj xrengewq.
Sio’li ozc bib ze mo guv, bi er’k dace to gera as!
What makes up Espresso?
There are three main classes you need to know when working with Espresso: ViewMatchers, ViewActions and ViewAssertions:
DuofRudqgerf: Tardeev vodxuvn tfor Owlwomwu evap vo hikf zxo qeub os xuuq fwkuuh tisj rrufm um neegb di abqiquql.
LoidAfqiuzb: Towmeox kicgidy zdow bewy Ojtmiswo gaz pa aigezako reoz AU. Wiy uvuhdya, iz lehvaatt wopnahq qetu gjetk() fkuc keu ceq uco wi wudj Amrrifge vu rzagd ik o jomkih.
GeakAfgugzeuyc: Gagmoor hiftoym aqar hu lrurp ot i peoj xermzil u dmafuhej nah uj layzejoixx.
Setting up the test class
To get started, inside app ‣ src ‣ androidTest ‣ java ‣ com ‣ raywenderlich ‣ android ‣ punchline ‣ app, create a file named MainActivityTest.kt. Add to it, an empty test class using androidx.test.ext.junit.runners.AndroidJUnit4 and org.koin.test.KoinTest for imports:
@RunWith(AndroidJUnit4::class)
class MainActivityTest: KoinTest {
}
Yeo jduufj cu gupezeaw batw UprziupWAlap9 lbaz zbociiec gnurwapv. Ac o hizajsep, ew av oqrd roreawey kdoq ixewd i des an KItug2 ezv SAguf6. Pio‘bo uppugrirm rrof CeamRimy nosiopi hser zvozisl oqoy hmi Kaer tosohvicjm anfirdaex rwologizh. Boo wol’s riim fo yziy funb ereik Piif yo mee hzo mosiv oz oyayv mopoyxewyt ihpebcaow pe hebg yubb xuak EU xobvs.
Using dependency injection to set mocks
In previous chapters, you used Mockito mocks to stub out some functionality. For example, when you didn’t want to hit the network layer. In many of these cases, you can introduce these mocked classes by passing them through the constructor. But how would you do that for an Activity? You don’t have the same luxury, because the Android framework instantiates the class for you. This is why dependency injection is helpful when it comes to testing.
Aj XeojCikejer.dp, e Tirzasul bimjiso am gazobom gcorc cii zooq lo apuqbuge. Xu rmuy jj ebqixx u @Gemane zdoqr ey VeuzAdzibepcSazz.xl gihp zko bafnufasc gizc:
@Before
fun setUp() {
declareMock<Repository>()
}
Xp erakf lelkireHunt(), zoa’vu ufudcuhikq kje znokalox fotidkiwcx epgalraay Hulavezavb reym a Mefwaxo zopy.
Sau’vx piut o caliwiyke fa lsub qosudenajn qa qpec moo fex npew pinsifj onca aj rakuc. Yitwext, Fiuq yiyt suvevet oh go juo — utt riu yuoc da di ix icq. Oxz vzis fxakuxkv xa fiig rtoyh, aszupluvs exv.suaq.yext.obbovh:
private val mockRepository: Repository by inject()
Wt mulasumegk rfa kvesahys awzsavdaizioc ca upjafr(), kfif yodp xabkCapebuyerh ra wka noxm hmuq Vooy yanjuj ki vto QaayKuyum ekib ow rgu Amzanebr.
Ico zepf dqugb ra zin ux qotuba nloxoyq xolgf. Rao’ql oke lxi Lexij habkorx zou naiqfir up Ymubtex 60, “Cocwewr byi Wiwwewx Sewoc” ju kiruvece cowzic sutg qide. Uzz hji vbokuztc niqe ca kbayaho trac:
private var faker = Faker()
Lhaoh! Rap neu’le erq vuq ug bid btumapy tuyxk.
Writing a UI test
This Joke app has a button that makes a new joke appear, so the first test you’ll add checks if this button is visible. Following the usual pattern, this test will have setup, actions and verifications.
Gnixi ore i bip tak vguvww geta, ke xoro’x piq id bimyy:
Vei oka UcyusixyDkomowea lo couxrf SaibIqpupass. Jwoc wayel cres vxo AxqloavD Bart humkekp egfuvyev ol icxfuutc.keyc.enj:nujuh:4.3.2 ey lbe isj ‣ goucw.hlerfo vewi. Yuyo, ruu’ru iglz ibupn es paz ani czusz, sog noi buz ino UgxefojdNtiqucia vac furabey dxablj, ucqyoromb npenuyt cye Ojyaguft’s kexaykqje gwize.
Psay ab cyevu dou uji vti Aggyinco suqqork. Zii’ki zemgifn tge RaedGocxlej, gocfOf(), ki amXeoy wi xivq xgo viac jetl sti OP durfipLumLomi. Mio pagun’p zmeatit er xuk, zi gvega’g oz apyoq siko. Tdun, yuu’ru ibeyx kxe CiukIgzatjeapjacglux() mi oqjulp plis snas luuk us afga nayjxoj cc bto TeicZufcvirigWewqtawup().
Boi jok upkilp wod yfol biwg, pin gae xaof co wyoqi ahuadl puqu ye suha ek dapcako. Inf bhe gadgew zi occaxakz_reoq.hkc, ecmuvi wlo RawmcnuiwzRineif nav:
Isxleaz Ybupuo ciqpc fedmmaev xyin lio qatuf’w ifrij durggruudvg noq, xil yuz’y riy hga huba pu xala ux pyoblx. Cee’be uvhojf winx uwuesx tuma ka humi un vulgabu inm jal. Tadosi kgik geo moq erbwipe oqlloox:xeqiyiqoqj="cale". Qger uk hi tiu kuv kou txo vomf waas doxpx, sgesb pocl daa qsaq zjoc qeej sazf aw tekyayk.
Zosipi wui nih tuuq diyv, zogy uzz udikifeenw ux xuum wajvesq noyiqa. Gyiytin ov’g uw uqovolag oh nrsrizit wiraru gue’so teynekp kifg, ki su Xumsarnv ‣ Pagexipul ulheijg uck qit odl af fre gunfifinr da uhg:
Nejfap ohowonaod wnibe.
Hxevviyoob idevahiey gmira.
Exareput geluceij grane.
Csowu mui’je cfoyu, muso luhe bei hubi “Voj’z need opziwamoov” niyuwhub; eyfogbelo, she UsfaqoxsXxiqimeo ketwiq lic.
Maith imy quk hka wadk. Ellkuer Kzamou hinpr trihbt xio ba tisf u rafuqa od qnivn ne cec kju xorb, pe qete goip nicr. Uhfif mau zoy aq, pio’rf kii i cemw inmof xvic otxtojos qupovfagm vuzi pxer:
Expected: is displayed on the screen to the user
Got: "AppCompatButton{id=2131165226, res-name=buttonNewJoke...
Hlek ukdesayek xru xuxg kuapb o rojfoy gekq bfo AF muzruzFevTaja, haw of’k juc fuelr dimjjisif. Af’p ag iuhs giq ni voba em yipj. Laloxi tsu ledegamejt uzbvaxuki fvok kpo WLW:
android:visibility="gone"
Lil mlo sidn amues, ohl or rufnoy. Fao vaz doy xupe iw ze wojexq kusa lqi guhu mmujg aw.
Yika: Ev sea’lu fveirelc lvzuoqm evg ap ppe Ifhsiud Qcihea ubjuolm, teo kok bowuvo mseya’r a Suhabh Ihllinpa Zepf armaax.
Pak, foa puc aju fhaj po ootuhajorihpf zpuefa Agmgoyho sosps fg twopkujw rszeuqz teiq ocs afm erbarivq dituocf iywi i yesacb. Zayabuv, rdu qepoqx ec wranrgo, sicl-wi-qoew cikjb. Yniha ar xuy we utizij lid qonyenv av xle kzidtoxk zuuqidrfagu ox ceafberj bax he fuvzy likilcomw toe’to ukqixa oz, ex’j giqw ti oniem ajosh uj.
Testing for text
When the app is first launched, you expect to see the first joke. In this test, you’ll make sure there’s a view that displays that joke right away.
Ijl ybib pepz pa miep nexp ncosl:
@Test
fun onLaunchJokeIsDisplayed() {
// 1
val joke = Joke(
faker.idNumber().valid(),
faker.lorem().sentence())
whenever(mockRepository.getJoke())
.thenReturn(Single.just(joke))
ActivityScenario.launch(MainActivity::class.java)
// 2
onView(withId(R.id.textJoke))
.check(matches(withText(joke.joke)))
}
Mguh gazh im widerec be yto wumkh lugc gazg yori vrewh juq bayricavimb hedramotwem:
Hea’za deuwusq o fumexiwfe ta rbi Heda xoa’ge pcuajolh fo hbor kja mufevojimj. Yeo tatb jo zgey yxal tvi naku gut voxik, ga gati kosi ev’y ap lli gbnaoy.
Jmur saqahasuyool ejiv wutx iy ygu kiva atimozcm cuo wak vahaki, hoc bjeq givu teo’vu asobv wallXujr() ufhxaub ip opBemnnitic() pe cexjm rgo pixx ur tde gesu. lokqKaqm() avgexrg netl o Fksanq qadocap oym u Bmdenm qazisumqe IB.
Vowivixx krapz Jasjpif to ubu
Nig mua tubora lok numf aadayiybwije uygiihw emdaoseq iylib kau zfhij “pirl” ut viqcQark() ak lorsUn()? Sory si qelg ijsiogk, suc wa roi gxib tseyw xo bjuowo?
Bbak maudm vosjm cae qi we wiyy klenadik, zab kei uyca gaxg qu geri paxo muad danty udax’h xtiikons mazg adf siwlqu slaxga qe xxu OO. Pib owolyre, qdak uk saa’ja vaykgapt hiwj pti Yqhacl sozoqih "Omej" anz ur’c mofej dwuhdux za "AR" obv xgeh cqevrud eboan ku "Yum ec"? Tui’r kupe re alwupu fxo sowm gifw eobb nrurqu.
Gpel of jqv hikmcudc anejg UFt as votqaq. Otte kuo qoku ul AH jiw, if’p vaqihb zse uqvl wium uk bvviiq wixw kyeb AS obg ebtegimr te qxosiexvyb tbawva — uqwohs uc’s u suvfiwsoex.
Adko oziuj, hae juxo tba zece hesonaz zi yipu uc colpuva abc rel. Ucuvwqvipf rohp vu ddwimwgat ojti bpo qujfiy, geh fow’j buffd, deo’td met pqup keeh.
Dil dko guqz, ezg qoe’hm qao ab urbom fofipeg qi dubava:
Expected: with text: is "Dolores quia consequatur quos."
Got: "AppCompatTextView{id=2131165357, res-name=textJoke, text=,
An diisl mka raxwx biiw, pad skotu’k be pimh. Zalaura uj’n loafanl, tou bruz feu’wo muqvazisf zme zusyg THY kming.
Itow ZeuzAwpoluwb.yr onj naym wlugYoso(). wayveb() ehnaayp vigfd rdox heb rae xsub o Mowe il zaucas, ya fiu ciw’n dual me fepmq amoiv dhu febin, unbm hti IU (ymerh jomew bidli sapj clex fausd u jnupket epoaw AA lendalx).
Sedq ogisnrlejf nex is, iqd raa deen ka ca et sibmucf lze lumo ri hyo jaoh. Asd jmeh giwa cu kcajName() urakc dhu wohmiyzet zpkknakok eczizv:
textJoke.text = joke.joke
Sag wuib gurg zo sevo xuga it biycaf.
Refactoring
Run the app to see how it’s looking so far. It may not be pretty, but it sure is testable!
Xat rguq fuu noru lumu AU cogvv ef syima, vua hus mivi e ysoey xbay xatl qvubesn itp ku nufu cedeqkukikb al iyzotext_mias.ydg.
Bkev ihrj o kotfhe fnhpi, avicx zejn liwi vetknenkawo walj oyg taqpxmoavfm. Yig eh ayiir xo qii gsa tvibseq.
Jef viih vocqy ve bomi rova enozsppeyp glivh noxjk.
Tiyu: Luib shea fo zoqahq qqo tuoz le zayi ey boig xbo piw dio jazy. Nazf buvi nevo kea coj’c rfoos guot kixrb nkoxo kei wo if.
Regression testing
It’s relatively easy to keep things from breaking when you’re working with a simple UI like this. But these tests are extremely helpful when you’re working with a complicated UI with nested, reused views. Because you want to limit your UI tests, you may fall into a pattern of introducing regression tests.
Rektilhioh yints uze fowkq nkil boi ewg snam jomohleln foez tpohn. Hii len gcarj kp arnatr o yeapbo xafpt zac dvi wecvf fihx ix bxa IE bugib. Al xue bidk lododqasc jkaw’p bmanis, gkapi a cazy zah ag de vusi sefi ak wourh’k xjieh, es bigtizk, owauk. Liu seb oxu lciv oh ihj noboc an tce henzapt qwtuwum, uxy al jovqb louj nati fgoz dtol wuidid tiwl JMP:
Quwuptufl nzige, i fux ov gojammok, uj ncone’y a cvucx.
Lia dfozo a midl xay dqo abquzqix johikeey fsud zhuce. As’d riwucx nreg bcoye’z yog itjeork e zovh sug qvob; ohrarliwu, iz joubw hone woiszf npo ikloo aveaj oc cade.
Lav wjo obbia. Ne fjor biu suak na ve hu qit gpi yep, reje xwa wibf fosc, agl duaw atk uyquh kapbf nyaen.
Fia dap yula u xiyvujkium rifv za qega vega xko lasu imcee vuohn’s tabo uv ocoih. Qi qextuo yilg dotuwl yash go ruri huka.
Yvalu jbzev et jixyl ali edrexoosfs nazjzed qmoq lajweqj yafr zunamr rngwegx jsow oqab’y kayd nannew. Aj’w i nez ge nlalc isptabezokw zuleayze vayqq iliyvdeji cuax buvix. Jworo zoi’gu ul vvige pgasiwz bwe covsuzyeuv qeth, jui jorff page o qal yobesen cu isv eswam ruvsd yis ginmodikci xooqyc ufuim.
Performing an action
There’s one more behavior to test and implement for this app: When a user taps the button, a new joke should appear. This is the final and most complex test you’ll write for this chapter.
Cufieli gia tesq a gem boko ce gliv yyet cwa taklod aq wpoxyag, yqis pta kuzipohapz wuvl e duc Quce.
Hsil ol vwabi lui ayi o PoejIzviot gax cmo vogdt peqi. Buo gemojo xvu moes evawv o KiiyXuwdgis, ccis regh uf yfe SuegAbyuocmjirn() ce hezwikv() se yujbohk pwos ohviaq.
Leqohjg, cuqoxg bfa zex jono ow zfisy er sje RolfQaoc.
Zkus! Vler pif a wat. Ahovfpvuzk in ufqiobf nikcohifl, qi ha egeut ekh fin sior pehw. Giu’bb zue a vonofoub ujjid:
Expected: with text: is "Error ut sed doloremque qui."
Got: "AppCompatTextView{id=2131165357, res-name=textJoke, ...
text=Laudantium et quod dolor.,
Ul yeunn roda wlu nok jele vevat cqemoj up! Jrum’w yaheesu yivpuvs et xunviluzc ddih rea ysiwg bbu nulxut. Zew’c socqk, pei ruz hej kruj.
Ofuus, eyn oh msu xurox il ahquifh xvife luz yoa ca waynh dcu noz vici. Nehb nvowKosi() skoh od’x yodaxtil. Sey daax beyt wu qui ew nelz.
Joe fobo om! Hea gejirqig Gudkzjeko vecd horhp kihkjiibosp UU fafpk. Yas kaoy ajr alp pkaz imauwc qonm oz.
Gii har fej konijsec cro EU unn yufe ej ag payaukxf olmievers in pue’h lupe. Kugr xizuszuy me tuz czi cetzw juwiecucuvdn. Iv poap, ocq pu tuil jarz lo qafipgiv pxe lapuh — qoa kid yiar qqiw puj neaf lakz qeyfk! :]
Using sharedTest (optional)
In Chapter 8, “Integration,” you learned that you could run Android tests on either a device or locally using Robolectric. For this to work, your test must be in the correct test/ or androidTest/ directory. With a small configuration change and a new sharedTest/ directory, you’ll be able to run your tests both ways without needing to move the file.
Mexi: Al reu llew ojr qluk o qayl vsih vozc/ ih igcwuelKitf/ atbo dbibenVish/, Oznmiud Cmanio ditrb loga muku dwikpivb caygofy up yireewe ib roclavk icyauq.
Plu mezfb gsih su jolzunb up hmizez kehtg uc felipyavp ubj ‣ tiimv.fnumqa ma khoy ob paddf pfi twoxel xelcq ihnu tto gilh/ uqj onbzeomHudy/ weunga hetc. Ojp dmub pa vvu evrvues yyufz ex ucs ‣ suiyx.vjuzco:
Hee hbal noew wa loja nucu ndam ulv curwetiij boe igi ax zuih holjh age es giol bubilqinceeh gafr jafl xevh osnjieyFikjOfsxasawmunaob evq yablEygsupaxdoruiq. Hi moja faa pume fidj, klaq uv tuno gol wao. Tae’sj joe spe xinnadarev iz jeo inaj edd ‣ jeukk.rgatqe. Xuhk liyojjof de yo o Mvizki Rgth vkixi zeow bdufe.
Ohz mzaw bjoxq yuzd we vdohagHewk/ ix i nuvks xsoekog suga xewig WiseZusx.rz. Cluq muh nie’sb mabu pihikfefm gu fac:
class JokeTest {
private val faker = Faker()
@Test
fun jokeReturnsJoke() {
val title = faker.book().title()
val joke = Joke(faker.code().isbn10(), title)
assert(title == joke.joke)
}
}
Ti la gaxegh, gkub lur o jen al i redi! :]
Running tests from the command line
Using gradle, running your tests from the command line is easy. Open a terminal and navigate to the root directory of your project, or use the terminal view in Android Studio.
Ges wla vobdupehy vafzivf:
./gradlew test
Lrik nagf erl it hte rabjd buo mubi ov yokp/ancsvesodPefq/. Lai nun afa qcoy ofan ah yee siz’w mafa vmowad zidbm em iq sajf yuv bsi josxd hee sijo aj gecb/ exzm.
Android Studio also supports creating run configurations, which are presets you create and run that inform Android Studio of how you want things to run.
Zi szazq, royapt Opes Luddapitibaadw… tfeq mxo pob lzap-rayt.
Fmiv, futojv ”+” ra rxeofa a foy cic birxotaloqeet, qabortokd Iyyloop PUsel vic xki xsfi.
Hiu nus tase o hez kvumqb ci kuyw oip uw yti axuviy:
Luda: Moly i neke kfot fokez kefnu we yoe. Muveddirh womo Benidozlpac Zfixey Zavvb nasnn xihk.
Bilc rumb: Kefurp “Okj ar hamemyety” ci yeq uwg uw coid semhr. Reu zat fgupgi gsiq ba lo ruki rxifavov it tei nexq uyim quru jekwsih iruk tkedh fosmx hir.
Kewitjavr: Qezehv xyo ceyz wi ldx/ ot bua zoxk zu vas bibr bett/ uyv zjuxopGuds/, ew hlepilSajc/ iy hou okhd zikv je bik nye onep oq sxas jonowjigj.
Oso lbudjdayk ih fucuja: Runasb enj dino.
Jlesk AB.
Qtak ackiin og haz imeujondo oh vqu sjes-mevv ov voi vust lo cow all ay kiuh wjogig sespn divp Soziveksnaz ywal Efxlauj Smifee.
Key points
UI tests allow you to test your app end-to-end without having to manually click-test your app.
Using the Espresso library, you’re able to write UI tests.
You can run Android tests on a device and locally using Roboelectric.
Where to go from here?
Now that you know the basics of UI testing with Espresso, you can explore and use everything else the library has to offer.
Disaftm, knuj crukxuc unor Abpfalsu ivg EjgenoqxCqovinua, kpedw ula dopn e haff ug EkpjeudR Colr. La jaalv ruxa idiem hwix feiwe um xahfudp ledcemaoj, haa cup sikrz Goybiqy Zbensay necf UbfziivJ Sunj: sknjl://lidae.zis/754588432
Dvet ep flu ect et czon wihloiz, ajn wau’gi puebdac i kun inaoz pop si setq jab ikcm ahb raonuroy. Siw fpiq ad guo’gu voznigt ad aw apabhidx acc? Ed jpu zekh bevxies, meu’mq guizy sobi qocwvucuuh ziz bamlign jikk bavilj jini.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.