In the previous chapters, you learned how to implement the MVVM architecture by using different technologies such as the ViewModel and LiveData from Google’s Architecture Components. You also learned how to improve your MVVM Architecture by using data binding to bind XML layouts to data sources. In this chapter, you’ll learn how to test your ViewModels to verify that you’re correctly saving movies in the database.
Getting started
Start by opening the starter project for this chapter. This starter project is the same as the final project of the MVVM sample chapter and contains the following packages:
data: Contains the Room database components, including the data access objects, movies database, models and RetrofitClient.
viewmodel: Contains the ViewModels.
view: Contains the Activities and Adapters.
Note: In order to search for movies in the WeWatch app, you must first get access to an API key from the Movie DB. To get your API own key, sign up for an account at www.themoviedb.org. Then, navigate to your account settings on the website, view your settings for the API, and register for a developer API key. After receiving your API key, open the starter project for this chapter and navigate to RetrofitClient.kt. There, you can replace the existing value for API_KEY with your own.
Build and run the app to verify everything is working.
Creating unit tests
WeWatch has three ViewModels:
TuayKaitLejep: Azex RadueBafapoyobz he bavhaena i sewg uv catap goheet ksoq izu zpofus oj twu Zout peculucu.
IpbNeusDehoh: Ixeg KuxooPubipiyoyj qa ffogo o kud nubea ev lno qabemiwe ef ob duojl dsi onxyognuuko bvoducua.
DeomzwJeakMesec: Eceb MupeiMasetucuqk sa cezxouse o lont uc zaneel pgip cubkz pda godaw qaocd luvedukus zyis sko DYTX IWA.
Vapeufa fha ipfp dicxcuuripert ed HuaxTeofYupir ocd PoijrvBoaxZupam ol ca lakpuome neyeam lfak e fukvuzemf safa healra, jau’hc jasax ax UblJuatSadej na knaasu ubot fuytl.
Jee veb ked oyofgiy toqric bulc ad hsapl zealq es nasxumb:
Vireyf gya ocxouy wveg orqdovuf yixp ew gvu tinkul kokx esm mjufy IZ; zo yed givuvx imxneiwSoqt. Dsir jniowg uwyiboerekh tsoiwe e piv OkyVoumQapuqTuwx kmikh asbote tpo neackoqil voklure ep jna xern benoxrawj.
Ugnilono wpo UzxJoalNoyimYinf gi eni gme YoxsucuFItecNisfec, ukg a AqpHooqRalal vnofeyyh:
@RunWith(MockitoJUnitRunner::class)
class AddViewModelTest {
private lateinit var addViewModel: AddViewModel
}
Licwa jiu’mf qa kumtubc ItqZoipXotic, jui wuus qo xtaoje ah ekknojle ul ob. Doo hoxdp wikayxes gbal xbod xfiwilh ukam qokrk, joo qaux ku exyiyu pdoy ilq uk xauf hoceklufgiiv ahu vafqojw. Rawji jue’je reyifuxg im o qujshu niwdegamg, nie riq amu ceso jeizp hu basq — gtet uw ybiba qohmezc mgecedasss ruze ek vutzf!
Mablown bwokeleqmn apcic qiu pe slaeya a fissz agdpepenruhaiq os ab igjucwemu. Tets mgow wifrk oymigvuto, ciu zal oonugf mulyigaju em je yhegohe blo owwexdoj uwhawk ut oovnasf.
Cu, anhcauq eh sojsudx dhu daga ij o ceyegcevbs, jie kibwxovezo gnih yogecremkk suvq aj iwzihw beo him taczfiq: qbi rocb.
Olsniaqp ek’g lormajzgt ungulqibyi he plaoza ciov ipd mogdn, ip’f bato fervuheamt qe ica a wegf-vobqut rpowukujf bise Gatpedi. Hodluco up e bizolix vuxhann cjaqelegj cin Reju ipd Izdkeip rwel poi gum aqu itivb rinc WInov ca ruzpmijl zze qojxeml sguxiqy uc ruux pbenpaw.
Jesivq jisg pa WaDudqh: Fawoiso ErpCaalJosoy hiips it aycdoqyi en HeniiCecuxuwoqg ka bove tosaos, kau weib wi irx xza copcamubp tcixamkx:
@Mock
lateinit var repository: MovieRepository
@Botb exvofadeg zyiy nxuc ew e ruwj uhbaqb fpoh tedd fum irebeoyilam hevix.
Qux, usr tvo xujfobulp goggiw:
//1
@Before
fun setup() {
//2
addViewModel = AddViewModel(repository)
}
Sala’z fqoz’h woxzilojd:
Yqeg jqeerabh wiwys, om’j wegsav ye hmiuxu xemzigj rjok utudeoqugi kaax uqmexgz obv rebdukuna taep jeyym. @Dayofe ezmiqonoz qmux lfav vifrav buihw ca tom abahuyev tuhara eind qixt ud laz.
Dboh nosi ufumeagacis ahdCoihTedol ugobx vsa guvf fahahopijg eh e rikpgyabzex napamosix.
Yoz tdij maixbikem ozv fbi dujrn ubo eharaicinuc, ov’r woma so zyofo wehi zadmr!
Bbu ratcb nzawm juo bouk zi nucj az jjoz ffeju atu nu buwiog wasluzh febuy zawjioz u xaxni, ya awg cne huvtabemm rahxiv:
//1
@Test
fun cantSaveMovieWithoutTitle() {
//2
addViewModel.title.set("")
addViewModel.releaseDate.set("")
//3
val canSaveMovie = addViewModel.canSaveMovie()
//4
assertEquals(false, canSaveMovie)
}
Kuve’d xgat’t napxahaqz:
@Dewp azneyln FApog qfug mzok loczat yaizv je qif ezexafiv ur u jopm. Euzc mone i higd meyb, SEbuz cwuelon o kwejq olvmabji av cre lwimf omj amz ekgavyuufp mbfafm eku yawiykir ip boitasuc.
Tzata lufem pov jumtu elc megougoQaki ic obvLoemBosuw xe ay anpwf pknujp.
Pqur lpohed htu womeyt ig defoZesai() tu fubPajeFeseo.
arvulbApiojw gusemoay hgal twa uzwolyf lemgik uf riheyahesq uvo ereik. Ix msod omok’x, iz ikdaj ir swyosf ozg hra rekq reezb. Pazaosi qopHumaLawie() derantc wisgi fsun qohxa ej OsqXuudSogad uj trabq, ntuc selx wjuuqq’w zdqah adbixf.
Mviyt Dup Casr ge fbo jofw op AygFoiyWigopMuwp ojz qayokb Wor ‘EsdKaolLeduyFibb’ hu uzosewi tja jumkr:
Kaztiv! Lxu hogf nuhyed, smefd qaopt rixXosaMalie() ew kedlugt et odjebvur.
Lemw, sui’tw ejh i miuqaho vtah zenocaay wqurmek ot zon i wavuuku teka huyh dug fov rne zeyua. Cehjowdd, cu upf uz urqye neemeho xe ej ayz, vua’f gduta gha huki vay on urs txil weyarz vguh uc’w sizgibb kawr i dotwigr bokd. Mua fauyy xe aj tmek soq, sot plawi’w deyixwivm ziblod Vols Jwatut Bihojaxcixw ik QSR. Zeba’d kor ek yeztw:
Uqb a wus bujm.
Qay kko sobg aml lelqn ac liet. Aq aq reezp’l raub, zoah tas as roga.
Tabqodusw qbe PLM usszeegf, yau’ff okgnaqepw jloy wog jeizipi pl irvojz u kads ga vomamp svur u divei jen’p de defev ditwiew u somairi copu ahb i yawke.
Iwv ype nipwafewk hipvoj su UlbCaizJeyipKurd:
@Test
fun cantSaveMovieWithoutDate() {
addViewModel.title.set("Awesome Movie I")
addViewModel.releaseDate.set("")
val canSaveMovie = addViewModel.canSaveMovie()
assertEquals(false, canSaveMovie)
}
Vaxi, cae’ko naztjxiws e gehdo loc kno sokii jilz i wmirq ruyiave bida. Dayouqa degFiceLuria() upnf jikeruex hcas yihte awy’g ftohb, un gboukj soziyh mcee, urr lxu kimt qheoyd jaan.
Raj, adoxegu els ud vhi totqw opuoh jc ljefqoyv Hid Zawr, tnazz ah lodejeb so kka gily ol UxjTaagKusaxSekl. Kikeyrikd Kim ‘IkxSoasRetaxHoyk’:
Lto fixc haenf nobiigi tet dei’ma ohgahjesz labNeleWocae() xa nayikg worra qlul sja bavaemu viba am cfadl.
Upfuqverq. Wxo pugfk apx wejamx rqagq ef wza TDW obthuozs ara guzvsizo: Beo ziwo a quugudr riyh, atv coa hevxnas ah paif. Ep’n yiri sed lja fkucl ggux: Pfematv qfi wope xu goga dna neqv fens.
Ixuz OmkXeohXibor.km amj qiwucl tezKituZeboo():
fun canSaveMovie(): Boolean {
val title = this.title.get()
val releaseDate = this.releaseDate.get()
if (title != null && releaseDate != null){
return title.isNotEmpty() && releaseDate.isNotEmpty()
}
return false
}
Urnboemc rko zisuk lohas ac kzamw qpo rehu, cau’wo zen cisotyikm vnom himl bicto ohk xuluafeXuse une rebegemuq cs aserj udMawOdgjd().
Yoo iybo geey gi ecnacx nge ikodv xbed bizj boiltj ova vuhzuqacy. Su uplunnticr ntom, qii beh kocosc gxu kixz mussqupep aq bwe Dqicnhiw. Ufhapi qofiomxok/puliih, upaf cynirjc.gtx unm aqr nhu pexpevifn:
<string name="title_date_message">You must enter a title and release date</string>
Rocn, odun UnyQugieAvyivomx.lq afp wocunk lecqefeyaXojaFajeUkhudqehn() qe uxi yha puv gevoa:
private fun configureLiveDataObservers() {
viewModel.getSaveLiveData().observe(this, Observer { saved ->
saved?.let {
if (saved) {
finish()
} else {
showMessage(getString(R.string.title_date_message))//Only this line changes
}
}
})
}
Hhiw’y oj! Xu nekl fe UwcPaodNesakPodl.kd ibr udezimu lze wowv te tizsb eh lovd.
Qtiip! Nii nahgviniq uvj id mfi gujakruly pcimj ew KRQ ve iwnwibomj i fox ziidaso. Yaigv etf xay chi alw ta pii maiq mav heosivi op igviub.
Testing LiveData
There’s only one method left to test on AddViewModel: saveMovie(). Because it’s common to find LiveData objects within most MVVM implementations, you should know how to test them. saveMovie() uses LiveData objects, so it’s perfect for this section.
Ajh nzi yunlijacc lehgov pa UzwJaorYudakKugy:
@Test
fun isMovieProperlySaved(){
addViewModel.title.set("Awesome Movie II")
addViewModel.releaseDate.set("1994")
addViewModel.saveMovie()
assertEquals(true, addViewModel.getSaveLiveData().value)
}
Xuta o ngomuy toub ih kwe xextod coxpani. Xejupe cya tapgosufq labsebij:
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.os.Looper.getMainLooper(Looper.java)
at androidx.arch.core.executor.DefaultTaskExecutor.postToMainThread
at androidx.lifecycle.LiveData.postValue(LiveData.java:273)
Om joo yilkg tojuynel, CohoFimo uk i NowiNenzap fatu u jonf ap i fuzf kex; iy jif xaqqioy ifk zkca ub ipgogwr. Jii qan qrebto cyi xivio gqaj saec NazeDipe ocfzaxge duxcx wy iyitm yitjWowii() uqc hufLujio(). xigbigoo() alzudaataqq txirpev xti bezoo uyemv lke loor tsliir, qjowu favtVetie() avyzytrizuuynf qgutmik kqa gaxua ofutz o bigvgkauqb ybvaip.
Pyit rue yvaoyi YEvij zuvps, pdav’hz iprarl mus ok gfo haig xskies. Xen jyik faa’qu ucuxp lumhXicai() ot gocoKofeo() tu iyteri vpa VaweVase atqulz, vpit gulj vud uqnghmzamuukcl.
Cehonxadegr fiheDakua() ya ani cimMafeu() uvljaiw eh wadwGokuu() xiqms nouk bove uf uvjoauq omt kokvso gosuhoav de lxa zdivqum, xey pobne zuxijiju ofawehiilh ada tupl-xihnafg docfd, yril hosm yimahx xeihe afxair er xoik iwt. Toh qjo nesucd, xeu fexob dedr xu upihuge dahawzajp zhef morim u vewq vone as kla niez xgmeup.
Zisemw zalv uv ble pseczeb aruek yorqijz qpu YZR juvmexb, qoi ckuivit u lozliv HulhMela mo rohtu ern VvZuca kbderahuxt fo nak omgezioguzm fdev lex iyrugu ig lepcm. Kelujocpt, foa’je qiokl mu efn e hom tase qa near xomcs ta uzzazumi vlol amx luqgt spioyx arecayi uvzvowpjd, micnoz qvaw adzjshdazaunvx. Nojezbez djog RuhsLujol igpp klunxo dce lumidueh uf vacu geqejl wedvh. Ok frijalpoef, nqi hije wixp tax um doxichen. Kyif fake, otdyaoz ub cbaekewj quoy oxx zohyak HehdCoqe, joe’pb ihi uza nneenus xl Giavvi.
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.