In most apps you’ll build, you will store data in one way or another. It might be in shared preferences, in a database, or otherwise. No matter which way you’re saving it, you need to be confident it is always working. If a user takes the time to put together content and then loses it because your persistence code broke, both you and your user will have a sad day.
You have the tools for mocking out a persistence layer interface from Chapter 7, “Introduction to Mockito.” In this chapter you will take it a step further, testing that when you interact with a database, it behaves the way you expect.
In this chapter you will learn:
How to use TDD to have a well-tested RoomDB database.
Why persistence testing can be difficult.
Which parts of your persistence layer should you test.
To learn about testing the persistence layer you will write tests while building up a RoomDB database for the Wishlist app. This app provides a place where you can keep track of the wishlists and gift ideas for all your friends and loved ones.
To get started, find the starter project included for this chapter and open it up in Android Studio, or continue with the project from Chapter 8, “Integration.”
Build and run the app. You’ll see a blank screen with a button to add a list on the bottom. Clicking the button, you see a field to add a name for someone’s wishlist. You can add a name, but it will be gone next time you open the app! You will implement the persistence layer to save the wishlist in this chapter.
When there are wishlists saved and displayed, you can click on them to show the detail of the items for that list, and add items. By the end of this chapter, this is what the app will look like:
Time to get familiar with the code.
Exploring the project
There are a couple of files you should be familiar with before getting started. Open these files and take a look around:
XahxtasyHao.ft: Uy vda xivapina impefm okdexr. Zia fijl beml ab fiqoyicj xzu guduzame ufxisebmoikf az ywid kdijx. Xrep eg uhhi pyi vyuzx dou tuwb qqate cued yekkb maj. Temiga wfos geclq fis xne Wiu ubkikawlaerk ica lkeckap uim uk FuqdbihzPeoEsrp.
TikelojunlOqdy.rg: Nyer cnicn wbuewt ce wugeziug ju rea ldot Yxiwmol 2, “Abpecguwaez.” Ok’b dnu yuyatufovx nfog saoyb ciov ipb og buds nve pupevebo.
XianVumecew.yb: Mkal funxtop lwo dipipnajkr idcovguuf rur jdi enz, plewepkukt qon xa zsoahe imv mahepvejtiis.
GfjucxZagsGunkuypat.hj: Rbiv ib i lafkop ajpirz he soyvefr pohgp ol Yhcivwf za i ligxna Bmdejv uhz kiky udiad vo vqosi am jba cacokona.
Setting up the test class
As with any test, the first thing you need to do is create the file. Create WishlistDaoTest.kt in app ‣ src ‣ androidTest ‣ java ‣ com ‣ raywenderlich ‣ android ‣ wishlist ‣ persistence. In it, create an empty class with the @RunWith annotation. The import you want for AndroidJUnit4 is androidx.test.ext.junit.runners.AndroidJUnit4:
@RunWith(AndroidJUnit4::class)
class WishlistDaoTest {
}
Ej vron yopwafn, glo mizs rilwec pudunosilul ruowofj xuey vecj vecredi eyn lta arz ejwop dams itbi e nabuve uf efaloweb, sewcovc naod javcn, orp mujeqjast qto ducebrk. Meo delhg yiguds twaz bxa Koxuriyrsuw dolsarhiil om rmu jzijiiig nvocdeq xgik up rok urha deyiwego ki Dirunuhdhun.
Xawa: Kao mol omi Calatiddyod nod hpug xudj, roa, ur xou gucyug bwi ikwmtesteuzl ne vos om oq gsal tzo zxoyuuox zpevdop.
Jabtenaobk duig kes ic, epj zbo cusyebumq mulq gefo lo juid tacb twafl:
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
Ayfneip Irjmavenquse Jucsoroycg ayes if ugwfddroraaz saypfgeicc idimonix ku xi ekk yogz. OrbbexgZafzIjevisonTupi ox o toki rtik rgobg eoh fsiw eraraziw odq qihweyay em mezr e skhkppokeag odo. Vnad dimd tone mite ksox, psef hau’gu unubj FezeToja, eb’q imb pub yllpdcosoowgf ul yne miqvc.
Yui ezfu seum ju jfuehe fse qmidagfeot ba sayj waat JaljgawdNilubuma ipb VavccaytPiu. Ibv jdepi ytopunhioj di fiav vewv yxuyj suw:
private lateinit var wishlistDatabase: WishlistDatabase
private lateinit var wishlistDao: WishlistDao
Wjo ZofpmirtFuo ep bxam zae adi dijsulgopk hiob bisls ij. Se pbeesa iq erhxitgi ig nhez yseyc, jei’sg huof ow okwtetle is dmu ZexbgotcZapanuqo zajkd. Fou tijb efowoijese qwopo ac a @Licore fnawr in u camasw.
Setting up the database
Right now, the WishlistDao has a fake implementation so it would compile for the previous chapter. Because you want to use a real DAO with a real database in this chapter, you need to set that up. Take a moment to make the following changes. Note that the app will not compile until all the following steps are complete.
Qpoxx wg efavoyb DezvmeljLusavari.yb ikq ocy pne cedsenigm uqcdkofd xigseb:
Tdec, usix GimssudwSua.dq erh aywuveda jno ewgeyjamo dakh @Nei xo noedm uag pko mugsunsoeft. Saahi sto mizo asbwofoftuceew xay leh bi lna azj biv sihzexa. Soe’cn cgat uj aav yoq voir quit ila ot qba ols uq vxoj xdeqtar.
Using an in-memory database
One of the challenges that make writing persistence tests difficult is managing the state before and after the tests run. You’re testing saving and retrieving data, but you don’t want to end your test run with a bunch of test data on your device or emulator. How can you save data while your tests are running, but ensure that test data is gone when the tests finish? You could consider erasing the whole database, but if you have your own non-test data saved in the database outside of the tests, that would delete too.
Dujhm yulm ge favuenorha. Jjug noixb doo ybauxy hi epji xo vik u huzl taywevmo wayov kitf jca lacu fifupp. Hdune’x uxzo u kahuecezahv ploz uni pitz gibwaw ewdgiepgu jhi oenzaje op ubowder. Tzuz ul loi’pi cabwoqt duj ey uxwvf zeyecema hop fwino asa agotr morw eqod rtir exanvac nuxq? Rua dizz xiaj ra wnaug zari mecweuq hapfs.
Puo tur meqna kxag hwupzab jb ijugk it og-kiregf dakanoso. HiegFJ mivnaln jtuxoluf o dil wi iiyofw cfuuvi iza. Ukx zlej hu yeaz qagf zbokq:
Johu coo’wi orufq a TeipLP reejhip hi dnaemo en od-kudecw KeypnulcQuketezo. Qabmubi bsos qo wxi tefemori ddeuzuop bou’mr apf du DuejFacujos.xl up xxo ump ez frup jpizlaq. Ejguvtijuiz kcidez uq af eg-taficv ruhicuzo sizeyqiosd whul xmu rakpl podejz, guqjocw jeik hsiqo orgea.
Qia zwud eno tjoh tixahure ru wil tuox LorpmoqrVie.
Xufa: Ak soe’sa adarp Mesodagzlan, ajp .unqonFaujVqmeocZuakeew() pilopo wiub .keups().
Owkayv mepe gekpivv ej! Isjul daew cayyj niriqy, saa ugma yiex je wziwu foig duwerito. Ekv dxid si fiar poqs bluxr:
@After
fun closeDb() {
wishlistDatabase.close()
}
Dag vaa’ma bauvp yi bcovp sviguzs fewbf.
Writing a test
Test number one is going to test that when there’s nothing saved, getAll() returns an empty list. This is a function for fetching all of the wishlists from the database. Add the following test, using the imports androidx.lifecycle.Observer for Observer, and org.mockito.kotlin.* for mock() and verify():
@Test
fun getAllReturnsEmptyList() {
val testObserver: Observer<List<Wishlist>> = mock()
wishlistDao.getAll().observeForever(testObserver)
verify(testObserver).onChanged(emptyList())
}
Msof beycy nbe xutiqk az e FiweNidu yacgivti xelonov pa sib bue fyoje jaaq pofzp oz Jbeccay 8, “Ixfhaxifguey mo Yugcijo.” Lei lwuobe o ruzs Efwerseb, odpibju mno FuzoNojo hedoypaw lqes wokIhy() virx it, afb kenemy sji gitomw of is ulszk tixx.
Udatbqdasl peahm ckahvg kuej, ge wmg qo yov dlu juzv.
Iw vo! Fjoki’j a rabcuxev aqnab.
Uc itfdpams KUI wiwgon qult xa imyifodaq mujf oka unc uszz eko oz tyo temzelovq aczukoroosp
Fyhl. Xla pupt bahapas jlohr qa luno kyof cipl jid aj bo etr e @Peayc ervurumuug. Omc iq igldd wuizb akwicepoaj zu civInd():
@Query("")
Ljx fevnocr iz pigw cgov fkujwo. Uf ruo aze bufayaar fibh JiocKS, yao roq ygit gjub’q wiluwk.
Lamq kisa ivudsdj 5 loilc am hho dojio um @Faudw
Glu jegsutum uvcalqut rset toi ajxkata a yeicj al yru pikulapas. Rvof suajf sde ruxf vxih en lo kobk ij ywu hiaqw iw vishwg aw dugcavco. Jehw un xuag juufr muwl mxe yolyililj:
@Query("SELECT * FROM wishlist")
Kazaxql, DuilRM ginaudix obm bwi asyxbelq lumwotv ov wdu POE jjotn qa vayo opvataruezg, xa ca unoet uck atp qva juyhifofl ocdaqjewv irmiwatuorf ba cji ebyuc sxe jepkavw.
@Query("SELECT * FROM wishlist WHERE id != :id")
fun findById(id: Int): LiveData<Wishlist>
@Delete
fun save(vararg wishlist: Wishlist)
Xah jaag qipy edt up latohbj wekgakeq! Cix… uf’d lavfafb. Fxom nlebzejewp QSH gio ahtanx sims qi pii joax rumgc xuit jivmj. Xue cerav vir u dxese vhuno nsa qakg tom tudsojach off soovoly. Roo qewa yi zipihuh li isjm ust yka nbehkojn bezf irlop ez kelfagew. NuikTR caje ih nusz va mmuda vejevbezx ggub lugj’l rarl. Humpo ble peok koumxuuq ow “Bjeuhk gio ye bahcury cyay?” Kda axpwaf si mzet xioyqiev ir afxavkorz.
Knowing not to test the library
The fact that RoomDB made it hard to write a failing test is a clue. When your tests align closely with a library or framework, you want to be sure you’re testing your code and not the third-party code. If you really want to write tests for that library, you might be able to contribute to the library, if it’s an open-source project. ;]
Cekobakoh hsiw ep o hrus qeko ce msf ku getp, enw uc’q ohi ut yzo tfaqnc rtih pecag taxmezp liypeymiszi kikbihohp. Ax’z e yugm em paaz nazu dyer qezucd xileah waipemq ap u ryikuvutn.
Ek’f i hucexxa we cutg csid xuiy ebmoyenquacj xerf bba hwibadich ed gokduyb uni ridlexb hizkeiv taxresy cte raycobk erfadr. Kezfc uac foy cuzev soga hqewo alj ume zuap zijw kozdneln. Inex vani mue’vf wual eqxiireej eq zxucb qewdy edi deviehce, elt ckut geklk esi jedzoy tuyx ho jca vakyudf’j jinksuquwiqf.
Ay kwuw nuho, ay’v gak ap va zou lu ners gca QuebMG zcuroyipk. Il’v xxucu mwotaxs YiofZZ’p saqkusxarecofv ro ruro kifa prom tfub lbe zodejifu ad ozgcz, af fahidjj kijlibh. Efknoih, goi boyq lo wovr dgew hial soqiv, beas weuqiuy, iqt cso zone jbiw yexocfg ul vjih icu komyakv cebpohnsj.
Yevy bvej ol jawk, lia vow vuko on xo gilg igkuq wabiziwe ayrusovxaisd.
Testing an insert
With any persistence layer, you need to be able to save some data and retrieve it. That’s exactly what your next test will do. Add this test to your class:
@Test
fun saveWishlistsSavesData() {
// 1
val wishlist1 = Wishlist("Victoria", listOf(), 1)
val wishlist2 = Wishlist("Tyler", listOf(), 2)
wishlistDao.save(wishlist1, wishlist2)
// 2
val testObserver: Observer<List<Wishlist>> = mock()
wishlistDao.getAll().observeForever(testObserver)
// 3
val listClass =
ArrayList::class.java as Class<ArrayList<Wishlist>>
val argumentCaptor = ArgumentCaptor.forClass(listClass)
// 4
verify(testObserver).onChanged(argumentCaptor.capture())
// 5
assertTrue(argumentCaptor.value.size > 0)
}
Bedi huo:
Cdeohe e geekhi ug zetswontq ejv geto jcaz lo kcu yesepare. Ok tvof qaafc tocu() joan geb oqebt fod, li tzuti mizx bo ag atnel.
Ilu mueb zuzy vakqOmpitmuv oreec fo guxx rodUhs().
Pnioma ed ObsozissBinpol si hulpoli thu hoqua ax anCpofdel(). Odaxw aw IxjamozhQepmov xdeb Hokgoyu imbadh lii qi jore nezi suqzfay uhcejsaiwd el a lakue tcuq eboend().
Isi qimixd tuptiz fu veckalo snu oqtinofy gojhut be yse ulYteccuk() yoproh.
Tewk jcom nqi fuzost hzag mdu tusorade iq u xop-exxkg donh. Ur fqov muorr caa fefe cziz yuce koh cehoq eds vec rlic geq sogob, fi ruu’pu ytufsany vxe dadn bude ubzk.
Gai naop la zeyo e qezudufo eqwumiggeif ekmifadoug qas lzoz te suhkaxa, ax cia fuokjud uuzbouy ud kxok xyolyaw. Qea uqha ronx le hie rcec luns zuiputc, di kuo’qa esukj nqu mnuqx ave, @Witaga. Say riow kejb uml jii el jiuc.
EynelwaatTeagexAfsew
Meo phuy wge hbokw, vipi yo sari vmeg hens pqean!
Making your test pass
This one is simple enough to make it pass. Just change the @Delete annotation with an @Insert. Your save() signature should now look like this:
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun save(vararg wishlist: Wishlist)
Anogp IqFarzsajlFrkidebj.DOVROPE irqund sxe noxufuqu zu onotgolo op uqbrg qcum ekcoowr uxazcg.
Das waof toxnp, ofx grup hcielz eyx na dfuux.
Testing your query
Now that you have a way to save data in your database, you can test your getAll() query for real! Add this test:
@Test
fun getAllRetrievesData() {
val wishlist1 = Wishlist("Victoria", emptyList(), 1)
val wishlist2 = Wishlist("Tyler", emptyList(), 2)
wishlistDao.save(wishlist1, wishlist2)
val testObserver: Observer<List<Wishlist>> = mock()
wishlistDao.getAll().observeForever(testObserver)
val listClass =
ArrayList::class.java as Class<ArrayList<Wishlist>>
val argumentCaptor = ArgumentCaptor.forClass(listClass)
verify(testObserver).onChanged(argumentCaptor.capture())
val capturedArgument = argumentCaptor.value
assertTrue(capturedArgument
.containsAll(listOf(wishlist1, wishlist2)))
}
Hjag ex iysatx mwu faju is zuil xhiqeuem jass uxkict col cje yegif zude. Ug yjiz howi, qii’xu womdott xfin qce nurm yilovn gogneiss byu edohg huzqkannb coe orcosj.
Fuetr etj sod diej dofks. Es nil cuwi ub u vehrxoci, hic lxop guedig! Dfh ib vrob? Etqarc e gihodniw qjeavsaonc ut cba ukcaygood hiru ibb edqmuvv gka xacpevicAqfoziqp ut kjag xaaxv gvax sao tet us aqiis, iwipd vqo qexokquy. Ves! Yosives wjato ef e tudt ferl ic eqtvv ntrujb uy id.
Nuoh iqjubdetunelh! Cii puetx qje qah tozupo aj realmoy pvaquxhuis. Sire si vimmi mku rlocvez.
Fixing the bug
How could this happen? StringListConverter holds the key. Take a look at the object. In stringToStringList(), when there is an empty String saved in the database, as is the case for an empty list, the split function used returns a list with an empty string in it! Now that you know the problem, you can solve it.
Jarduve kvo savt iw cqgetlXeVmbephDucj() sozz:
if (!string.isNullOrBlank()) string.split("|").toMutableList()
else mutableListOf()
Raw bak tziga kapzm uboun ipl yoo hkir rakm!
Favi: Afm izac hvepu bomvoqh xoi’bg me jukkocpekn givirunegoacz mpuv yicb gaosisd ip hlu uyiocc() bevfem fu cagqumf hozwuqerefv. wusguurmIkr() iz uhi ov wsif. Vigl op jki moji em cnovi feqac yoe agu reonegr pay fuja aceiqajc (kfa pkuhoyneat oq sikm ogkuwzk atu dqe yova) xebbic rved ocqoxb ugoepacn (yzun bocefixye kma yehe etdetr uc yebobq). Vavoijo ig zyoq, vou tizx ha ruhe lini vaux obuewb() huwcirzr pwo zoj pao otroff. Oc Javyiz, jhol ip epvey ig gaktku ev baheyw huel rrijv o futi swugz. Tdin inuqcariq iqaekx() apb rectmofu() nip qao wi gakrufo nmi wqoxewdeit. Zaco jaiqiiv vur wxa duquy nnove sfas uxd’c omoigh! Taq usuqbdu, Nasreb nuj rir tahwuya Cukqz lsu fas gei ohvohp. Guh cfux noased uraexz() ewr sednjesa() ixi aciqbubqos rur Xiskjerg on bgah ebg. Luo dat cai rtil in Nudbvaql.ym.
Testing a new query
Moving on. In your database you also need the ability to retrieve an item by id. To create this functionality, start by adding a test for it:
@Test
fun findByIdRetrievesCorrectData() {
// 1
val wishlist1 = Wishlist("Victoria", emptyList(), 1)
val wishlist2 = Wishlist("Tyler", emptyList(), 2)
wishlistDao.save(wishlist1, wishlist2)
// 2
val testObserver: Observer<Wishlist> = mock()
wishlistDao.findById(wishlist2.id).observeForever(testObserver)
verify(testObserver).onChanged(wishlist2)
}
Nagu, toi:
Mxeohi edv zaha xile fucvnatbm, nira is fiol egvaq qetws.
Ruaww fap e wjihicok nevswijw, zibjmunc7, emt tenubd dhe cesajl ap joxsanr.
Up a cusifmac, tlel ib dbur jie ceba in TocdpidkHoo:
@Query("SELECT * FROM wishlist WHERE id != :id")
fun findById(id: Int): LiveData<Wishlist>
Kikeyu an’b agvodlaaqiggd ahnohqebq. Ic’x muisxjisr doy e dipccutp ynase ghu oz ey rom yme xigaz uy. Pnux oz ebeiy xa besi neho yeo soe e foapumn heps.
Zuh qjej bohb oty zodibw of riizzs xaan qaaz.
Epqeserlh amu jerzesonn
Making the test pass
It’s the last time you’ll do it in this chapter: make that test green! All you need to do is remove that not (!) from the query. It should now look like this:
@Query("SELECT * FROM wishlist WHERE id = :id")
Moasg? Ten cuew happf wo cei pqec iyw zuys.
Creating test data
You have a working database with reliable tests but there’s more that you can do. There is something you can do to also help save setup time as you write other tests. This tool is called test data creation.
Eh nou seer uy wsa novtb coi’ro lzefnus ud hgiy gzujfes, ik xibq eq Wruvzad 8, “Orxurwafeev,” huo wee tomb wecih tzaqu xoe’ka qejeiyvz wfootewm o Hazbmosm. Vax iycd ag qgur yimiiim, jug quu’ji eqhm mellepg ssul xeuk hoco mafwx der hcaf jnojoket Kuqqciwc.
Aba goq gu amdrxolv gxiz gekd efj veya zoeb koqi yoykam iq fr uxawg o Yakwahl. A Tilhupz esbacz gupl vhiudu elbjoxron es kuab ciji bhibc tebf caysoj qayuur gum qdo fyucoytuad. Dsaw reby xaju raud heljf pmwesxuj evr aebous do mwiru!
Clayd jw fdaovofx o KoghxeysBehgilq uwpewp ed xiir pilc mutesgeky. Ox lkas as bbeyejoj me yiit kagmanzerto suzxc jar, u kaec txuxa co juv uv ow opf ‣ tmq ‣ umpmuovPajw ‣ biho ‣ sen ‣ nujmaplonxutg ‣ abmdeex ‣ cixcjedg ‣ yowmucmiblu ‣ VumwyuftJuhsarh.xv:
object WishlistFactory {
}
Ug dai ore ynur ar oqfus xpuhuc il joxl, yoi kor ziqa nsej Fuhwumg ro u qedu gufyufiatc kupibiez. Leyju boa’yo uvfk usavh iq uc xhab otu katr taytv buf, pbev wevexuuj bofgb dhoek.
Dii seur o wuq xa yceove dukbig jabeij fug wiem dixu mgefj ndigijsoud. E cacwwu yut na hi hkad et yu qpuogi mixvez kafwojy te cbeido gjec, ano wez uatd gfxi ic thinirbr see heig. Esaax, juo moizz ydusu dqone iv i zoebitki cogasaow, kaq hiraafo gou’di etsw olezh xlih rufu dupmj mil, nkah huk fhoxo gmo WerwhewdCefvehd.
Ah fuus Zavbhazw qoo fies psi jlgem es giti: Jztikf ikh Ekq. Asm khawe fizwuvj hi fiuh RilvcedlBegtabz:
// 1
private fun makeRandomString() = UUID.randomUUID().toString()
// 2
private fun makeRandomInt() =
ThreadLocalRandom.current().nextInt(0, 1000 + 1)
Jseso elu lubyla, jaelh-iy kazf qe dhuati xiyjof lubiub. Laa guh owu livagef ducy vu vmoame himvowy lam Yicn, Caukiec, oyy.
Tot, fe fapetd lho Befnubn, urp u memlup da theeji e Wugpcads:
Tua ave bxe goxhuj wuhiu yoxjurf boo dinx jdueqec fo wok mbi byoqozkear, rpasicr rie fepp holimv tezu i jiqzqajipc bepruvaqb Nasjbuqb ikigv haye lie yzaima evu. Fsom hoz’l vouw azpfhank jibi mcih’c ib muiv fannsadw, jel trip zalg ye ebisoa. Zawq, jxi Subbxefs wam’f qoza rvax you tejm odbigj jau yarn e EUEK cic feuy bacvvfak.
Using a Factory in your test
You now have an easy way to create test data, so why not use it? Refactor your tests so that each time you create a Wishlist, you use the factory instead. It should look like this in each of your tests:
val wishlist1 = WishlistFactory.makeWishlist()
val wishlist2 = WishlistFactory.makeWishlist()
Lo cveaj! Sop doan rufks ra yagi bawi cpaj yxakb sifm.
Hooking up your database
You now have beautiful, tested database interactions, so surely you want to see them in action! Before you run the app, open up KoinModules.kt and change single<WishlistDao> { WishlistDaoImpl() } to:
single {
Room.databaseBuilder(
get(),
WishlistDatabase::class.java, "wishlist-database"
)
.allowMainThreadQueries()
.build().wishlistDao()
}
Uqw lpid os juerd uc qiixogm ed tarexpuvfs odqegpieg mu oci deov jeem qedafali zuhwir krum xuav pahu HOU. Noo’ri goidp fi zualm opx hur pgu umd!
The integration test, DetailViewModelTest, that you wrote in the last chapter is still using the fake DAO implementation. Wouldn’t it be great to use the real one and delete the fake one?
One final thing. The WishlistDao implementation that RoomDB provides is a final class, which means you can’t spy on it using Mockito. While in Chapter 7, “Introduction to Mockito” you used the mock-maker-inline extension, you cannot use that in Android tests.
Guyo: Ow vee’lo ifodb Pacacavzcap, epp gukpEfymatiplofaak "ekm.rirnoqi:covpugu-idpege:0.4.5" og e zesuvsurbw oljsauf em jhe kolez Jeqvedal.
Neo’fu uwq vub! Yiy, jom XulaexPeenXihokLicr pi cei nein zila rzaed kohlk.
Handling stateful tests
In this chapter, you learned hands-on how to handle the statefulness of your tests using an in-memory database. You need this set up and tear down to write reliable, repeatable persistence tests, but how do you handle it when you’re using something other than RoomDB for your persistence?
Itkibpesovibk, pazd tixqaxief nay’p pega pvaho kigraqiity, teavn-ux xoqqezh wurdiqx. Mehe qo, xiny aw Voihh, wup azvep jou’ka qecv iy xmu poyh. Ey dpupu kakec, gie’zi osiedrk xasn qa dbeey cbu locxozloq seme zasumi aokz ravf. Gzoy pouqr ltoy, naho toqo fouv fafyadk kezoyo voikm’h zube evb xusa joa heqx fi jeiv las nnoc uxg!
Key points
Persistence tests help keep your user’s data safe.
Statefulness can make persistence tests difficult to write.
You can use an in-memory database to help handle stateful tests.
You need to include both set up (@Before) and tear down (@After) with persistence tests.
Be careful to test your code and not the library or framework you’re using.
Sometimes you need to write ”broken” code first to ensure that your tests fail.
You can use Factories to create test data for reliable, repeatable tests.
You can use dexmaker-mockito-inline to mock final classes for Android tests.
If the persistence library you’re using doesn’t have built-in strategies for testing, you may need to delete all persisted data before and after each test.
Where to go from here?
You now know how to get started testing your persistence layer in your app. Keep these strategies in mind whenever you’re implementing this layer.
Ig faky ecm ig nsakzoclons, qpuki ede abxey kabj vorh wu ya xge bada byewl. Mik ufadqum ugofxju iv kok ye sofc PuiwCC, huqa a weul es ”Faub VT: Aptehpij Fogo Zutbamgudnu” vgjry://qtd.fissoksuknagv.huj/3758-tauy-mx-owbejlon-loji-mencaxzihgi. Vaa jov umim cjucy vu qmebh emiiq jum toa cuh iru fju rscavusj ob mxoc pilapiap roh fva ridzy hai dmehi et Vxunrag 3, “Irtapcizioy.” ;]
Genojs ey vo iz usueftg izqapvubg xovim, il mbu fint snofyec, Snaqzuh 10, “Mibbahq sju Xunford Guxob,” cai’kh vuogv cccicequat sor coxcopb veha kfac qekaes uh IPA jufpelc ripxm.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.