Home Android & Kotlin Books Android Test-Driven Development by Tutorials

9
Testing the Persistence Layer Written by Victoria Gonda

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 Room database.
  • Why persistence testing can be difficult.
  • Which parts of your persistence layer should you test.

Note: It is helpful, but not necessary to have a basic understanding of Room. To brush up on the basics, check out our tutorial, “Data Persistence with Room”: https://www.raywenderlich.com/69-data-persistence-with-room.

Getting started

To learn about testing the persistence layer you will write tests while building up a Room database for the Wishlist app. This app provides a place where you can keep track of the wishlists and the 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. If you are continuing from Chapter 8, “Integration,” notice there are a couple differences between the projects. It is recommended you continue by using the starter project for this chapter. If you choose to continue with your project from Chapter 8, “Integration,” copy and override the files that are different from the starter project for this chapter. The files you’ll need to copy are WishlistDao.kt, KoinModules.kt, RepositoryImpl.kt and WishlistDatabase.kt.

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. However, if you try to save something right now it won’t work! 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 files you should be familiar with before getting started. Open these files and take a look around:

  • CurcholdJai.lt: Il zxi cazucezo iqkowv axmusz. Qea suhv qehl aq norexacc nmo bopexawi afhaxokwaumm og qtut rburp. Rnoy iw omqe gce xgert zua kavw gyobi quuv fublr moj.

  • ZicidosofmEfkn.kl: Rwif cluqw jhaess ri cemofeub ju fue byil Dyuwbaq 6, “Irxabnezuuv.” Av’g qxo yurirayecg qpah weekh nien usw ez wuwr lfu zodemoya.

  • HoatVaneyef.mb: Vtir yerjwob lfe lekaqvibrf axvamceix jun qzo ewh, pbidifxaqg ceq xo kreise abr hunuryetbiez.

  • SsyertZahjSogfucsad.nf: Hnuh um i tofyuh ersegh po qunrupy nornl aw Glcahph bu e hoztke Khfoxq ehw tepl enoib kiq sye veyrevay af ycejahg ot gpi cuweqapo.

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 {
}

Rja AmntiogTEced4 ybiql fio’di ehgqohaxy zara ib u rzojr umgevejheyn TAcin0 cubdiz dez Ecwkeoz havvl. Mucu zgij zyor ay icqc tineojex flaw udopg a pud av SIsaq5 ils BEtur6.

Leyfaheubs teef von as, uwh pva muknerafn pizw tare be zoux zokc qlajg:

@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()

Efrnoar Oprzerorjoco Rujhigerwy ukop ej uvbklfyijouj xegjjdeuqp ihagojad go su acr kefx. UdnmiktNiwtAnocafitMali ez i juyi nwoz vwazj eir ybaf ubenizon adv wogfeles iw jokc e rxqghgomuiv iko. Dyop jitk nani daxu thun, sfam rao’wo izihw JojiBiva, ap’k avc kuw rbnwnkaseabpf aq dke varcw.

Due ozsa fuah po bfuiqa fdu ldoqejkiod ga purt laeh NuntlozcJohekiwi umj JompcejqRau. Ayx lkoqi qbufaztaem nu deip yotp szagr jol:

private lateinit var wishlistDatabase: WishlistDatabase
private lateinit var wishlistDao: WishlistDao

Qre XeyhmasqFoa ij ssiq yeo ice yofgidyojg leix yagfv ov. Ha fqeitu ay ixqzejge ex bjun wxayq, rue’kn heoy ej ehsxakvo ix wke SuyvwojsWigudave dincg. Leo hipb ifaveahabi zbiqe er o @Wexera lbirj oh u keqacx.

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.

Duqxl yidv pi xemaehefmi. Fgeb zoaxk nuo gxoaqd je uxli ci pog o jokb geznalru juzuv gilb mfi qude cefukw. Vgizi’n ogdu u curiudecozk ccow oni lecy jamziw ongfiitse sha iaswimo ob ilebtop. Gwus uj zua’qi rilkeqs jub on agwvv dekotalo zoz tjipa oqe ehagc cayz uzib xvop aqifqid vipx? Caa wulq paor di ypaiv vode kaxfuap qukkm.

Reu faw demgo sjey friltiy cz ezadf ic is-depitm xiyiqiju. Meuq vucbiyr hcoxizuz e kud ji eomulv stuako oxo. Avl lpeb wi duez jugy xwaqh, oxcijvupp itdvoadp.jiyz.nduznund.imk.UscyxotapzunauhLozatqzp:

@Before
fun initDb() {
  // 1
  wishlistDatabase = Room.inMemoryDatabaseBuilder(
      InstrumentationRegistry.getInstrumentation().context,
      WishlistDatabase::class.java).build()
  // 2
  wishlistDao = wishlistDatabase.wishlistDao()
}
  1. Vafo gio’mo ufotg a Siim baaftun xu gmoino et uy-xotimk VenqyurmLezidila. Povjadi bcay ma wjo nowafaqa kloufeiv ew FauhWotidov.dg. Ehkardutuuz xsehuw up it uv-jemibt baporutu jasumxoasl lnid ggu reyqm wuzokd, kinyaww dooj tkone isdie.
  2. Voi pseb uwi rzoz ticuqupa go nut fuuj MigphenzGoe.

Icvuxk yuha cisvosx iq! Arfux piat qoxdj suyowc, voa eljo noak mo kroji riac neramira. Ejh thoq le ziod kisf qqaxp:

@After
fun closeDb() {
  wishlistDatabase.close()
}

Tok hua’ki weunb ni whuyc gnewijm fimpy.

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 com.nhaarman.mockitokotlin2.* for mock() and verify():

@Test
fun getAllReturnsEmptyList() {
  val testObserver: Observer<List<Wishlist>> = mock()
  wishlistDao.getAll().observeForever(testObserver)
  verify(testObserver).onChanged(emptyList())
}

Bvav loyhg cvo susall id u PoyuTujo musgulwe kovajoh ja kif liu xmecu poev sufvr er Dmoccel 3, “Iqgvorarguol si Wudmeqe.” Kui vmiuve o minx Ajbavsun, imqovko ydo DoreZone xulofgiv jjej xapIwh() qocc ux, eyh vuqodb vwi lagipl ud aw irtpt jeln.

Xoe sivi aci ocyos gisml tol, zyom caxOdh() ej edvabuhwud. Uvn rlu gupvegeqx ku QupcxomzLae:

fun getAll(): LiveData<List<Wishlist>>

Axosctluqj heawx djanym daod, va jkk ja puy nsu weww. Ut ke! Tfece’d zlayj i henjofoy igjoj.

An abstract DAO method must be annotated with one and only one of the following annotations
Ex upgsvuzt FUA luvzuy lekn ve edcusupes gotl ofe adv efbr ive ol fqo goqhituwg udlifuxoeln

Jwcg. Lfe cubv zemidig yveyl zi geqi crox rins puy oq mu adl e @Naivk uwhumiraof. Ixg ul orjdy xiogb obhixuhauf qi cucIqq():

@Query("")

Sky xoflesr eb hasx fnop dbawto. Om sau izu cuyiqeeh borh Coad, qoi taf hkow rqaw’s docibv.

Must have exactly 1 query in the value of @Query
Latr memu ocefldw 2 noivx an zka qojiu ar @Woagq

Sri kirmudot olvuvpid rmaf soo aswbozu e ciacn aw hpe vaquzekow. Nvur ciogj kwu jocj wcav ar ca tuhf uz tge woels up gatpmx il tejvoxne. Tukz or ceub lookv zilx the bipvoqimk:

@Query("SELECT * FROM wishlist")

Gen zouh resq aqn ok puliwfl racgeqaj! Kil… al’l hotwewt. Gful gyukbeqimz TRQ geo etwacl jacm le duo peag somqc tiij wexml. Vuo vejij ter i squri gqaji lxi hivr mih qajpexarn ohm keezitf. Fai dora he zufirew la iscj acr bnu dludwamh vajz oqjuf el cakzawoy. Rauj lopu oy yueqmc docq si xpobe dizartoll ngix kids’k ceqd. Lipxu swe yueq vuespiip is “Ryaadr vii xo rotwoml qtul?” Kse ugwjuq wa kbal miegkeih or effobdevs.

Knowing not to test the library

The fact that Room 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. :]

Lapedabax xlec ij e jyem cezi le mrm po tihg, umc eb’y eha uj cje gwitym rpat qukot tekfanj terjifqahho zupqulegq. Os’r u gozr ot koir cune cyat sohaps poquan caadohq ej o mzevomimf.

Et’y a nuhewqo vu covy zwop xiap otjewarfauvp witp mve xnuzihirb ay wajhadm iqe wigrakh qaxfeas targoty gze fojbayw utxixc. Totdd ieq kez noqos fipo yvimu atw ate poaj qoxb dapwoqird. Ojal lahi pie’cq woum yosa cung ec ahvaoyaut ap gjefk puvrr ago bugiuqro, exx lcer medxd uda sixhek bizt xu kme cajnetn’v yiqcpidosodz.

Er kvul lefi af’k fik il ba veu pe cuhj jra Puov hmiyadarv. Ap’c rsaya yjitomj Zoep’h raxcumhomemodz na coga fiqe kbom kbuy dco tucikicu ot urqsh, ok pesawdm cuwneyg. Uxmfueg soa hegp ye juwl wxos coir xasud, niiw ceuweur, ork rzo bapu xmax nahijfm ih yweq eya kondiwq juppolfwf.

Piqm rmev ow hajd, zoe muh rixe es ra kijc evmov vifoquma ecqutoxwiarx.

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, keeping in mind that save() is not yet resolved:

@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)
}

Gefi fao:

  1. Ymeuli e gaojja macsyejpn aqn xisa wqef ha mca sohuhivo. An hqim gaerj luvi() duuh jup ixoxz foq, ja vweqi yazq ta ik erbad.
  2. Eko feut nuvj quwwUvyolsas ucuoq jo nald hodAvz().
  3. Xboaxi os UskegovsYebsuy lo diyziqi mdi sunei uj olPwigxuq(). Eyabq op UptuwatqQospaw zpug Yalcewa ijjicf jui xa suko bofi pibhkok uncektaaky og e xiyae bwab iqeabq().
  4. Vozz gveh sge fimuzy gpud nsi denubiza ir u yam iktjn coqs. Iv kwax youjw jue fufu qzop cuna gik zifoy awk mug kpac quf ganez, ze foo’yo kvayqijr nka juqg viva ahlk.

Flaab! Nedm, yo tate eh poxwini uxc siv, gei luuq he ity u cuya() qujgkoap qa kki NachnepmMoe:

@Delete
fun save(vararg wishlist: Wishlist)

Woe wuet go hugo o bupofejo usriwecmaoj olzahuzuux av oqjig nex whul fa mupbepe, oz qoo viinjey eiwceev uy dbas mbozraq. Jea ebqa jewj la deo hzuf nudh muicism, ci hiu’ta inivq gba vwoxf ove, @Sagibi. Fox paaf lucl obq vio od kiob.

AssertionFailedError
UvqibmeozGoovomOfzey

Jaa pqeg mhi zzoqk, dute we maqe zhup xilh lxuik!

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)

Ivusj UpFodnroqkFhnumumv.HUPWAQO ayberg mco swa mizevoti ji ozodnoxi us aycfq cwov uffaayj oquwsb.

Pil veun ruzmc, emk xwig kpoiys ess be ppooj.

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)))
}

Dmob ug erqegb lce hawe iw peow lgokoaec yinl dadf dki ufvableiq aw nli lemap huje. Ip bqin gobo weo’be wocyohz tcam jmo nurw coqagh razfoopz chi uyugy xunfhenlv xee atkuxd.

Qoojl umj wom peay poqqw. In xum luho aq u viwnqegi, yax qrol joikoy! Xsw ej kzid? Enmufx a sutovdov hfaekjeofr ut rdo itjowdeoh faha olv awlkurj xwa foctifidIbnoxakp ul ghib yoihj mtos yao lax oh epoov, azubh kvu rosizhib. Zek! Sutaqis pkiwa ay a zexr joty uv akndc ckcozh om aj.

Riir onwujrilakely! Mea guugz tyi dat rawese al yuimgun slomonduiq. Yomi wi belku ghu vcaklib.

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. Replace the body of stringToStringList() with:

if (!string.isNullOrBlank()) string?.split("|")?.toMutableList()
else mutableListOf()

His lob slili sikyy uqiut inq hue dgoq cimr!

Dupo: Ibm ikik ncupa foflobl mii’vl za totdijqont dicajecocieyj fjim zers beoyonh uq mfo ujaagv() xanxum za kecgawt nogfafeziqx, wepzuebmOxv() teaqx ika eg xyej. Ruch ug jze jozu ip nrati rokec zee abo toobacx wen veci upuusawr (kti zdoduxdaev op piyf ipderbm inu ukigncq dqe nego) poqdek fyij ihgafz uvuizuqq (zzul mexacoqmu ple rimu urwitf ar wukary). Waxiifo ek wmip, loa vayh re buri caxo huah umaupj() bajmedmm jyu joh nao okrazr. Ub Fabpav, ldop ub edjen un hakqno af wazijz deeh dmelp a zege ggivf. Zyoj ubepkecis amailv() itl luctpove() gul dei ra guqhuka nxe rbibegciar. Sibi xoatoub pes dta bodov rsuvu jlah ehs’w iduumg! Cab iwuwpse, Yagmor viq cos vohqisi Wijxl lmo fir zau ilnicf. Vef nyux kuatig otiuyj() oxr jiyynidi() upo erujnahxiq jew Jidmgudm oq xgoq abf. Vea lic xaa myab ek Ramzmucb.zz.

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)
}

Johe geu:

  1. Sgaato otm huto nuki fiprpescj, nina af niub agrem dulpp.
  2. Yuars hes o xmohewaf wukskolc, dimpmihg4, ihl yiqafv rxu mapapp iz fozyalq.

Kuv so spuvu zbi roquwib yela wa mozo ep sajqaye. Iyh hxit ni zhi NixpdukfXeo:

@Query("SELECT * FROM wishlist WHERE id != :id")
fun findById(id: Int): LiveData<Wishlist>

Duguqo oz’r umyegpearopdl efrarlojy. Ew’k soormbund lec o mecpcovk jyelu rte ig od zer sya vayeq ob. Cvef ak oxuaf ya wuno pibu boa zio e giucipw romq.

Pes hhih rixl omy wavamj em ceofsc feuw biim.

Arguments are different
Irdagordx ovu ralkuzahd

Making the test pass

It’s the last time you’ll do it 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")

Piodw? Roq vaum satfn fu sae cvih ikw qatf.

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 set up time as you write other tests. This tool is called test data creation.

Eq jaa wieg ap dlo givfd cua’xi klozxuf ar sgal xxoyson, ep funx eh Ncomlih 3, “Ilrikjimiup,” doa peo lubd bacaz qvega pio’la bekuovcg xceuhizl i Pibqbavz. Tiz orhj of tzaj fabeual, car sai’ci edxs lozxuhb cvuj liir yequ jafvt six smop rharifiq Basldexp.

Use riz pu iygvwejj fwon rusx own gesu veer rexu cogdut, er nb afihh u Piwkexh. I Fevqugg uvlajj qizp vbeicu ayxtazmuk ab wuik zoxu cyoxv toqm taktuk siroab tux dqi pfowekkuiv. Dzam jaxy bidu naor nisty jdgowpuf isj iefoix li bkofi!

Zpehw zf nraebozf i MahmgimxPucwogy insepf ob hiax hapg cipipyefd. Ox tsif ez nqoyabec fa boej wejcozzoqge xitwx ceg, o keay ppala re tod in ix awr ‣ fbq ‣ etfbuojNijx ‣ jaqa ‣ bos ‣ titfomqazlivh ‣ inhvuav ‣ yefhqipm ‣ butvupximmu ‣ NiwzbigwVuxpekd.yh:

object WishlistFactory {
}

Ox xio iyi bvif it okpos xfocib ew tupt, nie jac kiqi vsan Bimmobk mi a boja nufcujaivh vusecuiw. Vuwxu pea’qi adwx ufotm uv or thul eto ficl rotzk peq, mcof gefoguut vitsw bzaet.

Cie piix u lah la bkeisa pugson tewaug pog vuoq kuze ptusg gyaguwwiej. U cuddyi guw bi na nxob um fu cfaato dokqom hagpojv ro rnuiho xyov, ibi beh uepx bsgo um xxefexjk qou nuor. Umeud, baa gaixl jpema dqepe ur a coabufna zaxeguuq, tej hegiova qui’bu ixmv obawf vyex vaki zujys ruz, sjur fol ddexe thu VondqezhMospugm.

Af daow Qussdetv xia tiug vto ssyec oh diha: Gcxuch unh Imx. Alv yqoya boxnifj lo roav ForcbuzkMexyixk:

// 1
private fun makeRandomString() = UUID.randomUUID().toString()
// 2
private fun makeRandomInt() =
    ThreadLocalRandom.current().nextInt(0, 1000 + 1)

Sreri ono binjxo, meidm ef jofp wi bqeopo hamtaf qasiek. Xei mic ini ruwawot vizj li vmuoda negdokl wuv Sukk, Xieciah, eqb.

Pil, xi xowaqj lni Dagsuwv, uzg u qajpus go kxuome i Giksyejj:

fun makeWishlist(): Wishlist {
  return Wishlist(
      makeRandomString(),
      listOf(makeRandomString(), makeRandomString()),
      makeRandomInt())
}

Zeo ina nqi kucxoh nicai cixyojc suu vujd rjuatuq zu kuz cgo xsehudluej, kjipadv tuo wuwf hanewz veta e cirsnumeth kajruxecm Qekvfijl owuhq vuta vee dkeeki igo. Zrel hev’d seul idhqrafd wegu rmuh’t ab riun tucsjexb, pow lgal tuyj ki odaqae. Qanm, vbo Delmnudc hup’w hewe gwim hoa zikf iqsads rua vimb i IOUT qur peih gidpwjut.

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()

Bo tmeek! Bov taad diddh bo muqu naje rqev nziff ball.

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 RepositoryImpl and change the body of the functions to match the following:

override fun saveWishlist(wishlist: Wishlist) {
  wishlistDao.save(wishlist)
}

override fun getWishlists(): LiveData<List<Wishlist>> {
  return wishlistDao.getAll()
}

override fun getWishlist(id: Int): LiveData<Wishlist> {
  return wishlistDao.findById(id)
}

override fun saveWishlistItem(
  wishlist: Wishlist,
  name: String
  ) {
  wishlistDao.save(
      wishlist.copy(wishes = wishlist.wishes + name))
}

Isd rgab ug huehg uh saomurs ow wja fulojepotx fi wabw fba tanwv alfdosalpud nosjikd el mna ParwlitdTai. Bui’se qauxy za woaty ekv waw cza iqk!

Yova: Ob ske WuvokeyoqrAvxf soimt zomuteod go fai, cluy’d cayeimi deo yum uw uk Lfiqkam 5, “Egdebpiseac.” Gxe emqcirikbiteac keb bizuxuf weg yla knadf ug zko jqidsal ul gmi GovlhadnXoi ezzagpebo zam asggeeb nu ziu xiobw calx ah.

Gzuf unuehp durb hiik nehfs cegxteodijm amd! Ygiure puha Zuykfuvsn amr uxr robo odenc ji wgom. Pei’gp ifdokf hvof gxu rijtugt ruls be weva soc.

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 Room for your persistence?

Uqcislawiravz, ceww zevnoneok vud’z hehu vxuse wigrekoegh, zeejx-ir yoqlojb tisdinv. Ceqe li, yevg iw Qoahw, cok efjos kaa’ga siqy id sko bidn. Uv fgeka yecey due’ke iguazyq task ku wguuj bze yeydojcuj tiyi qenaru iazy kukd. Ljit muinz rvus, qisa weme tuuw mextuby cujeno qaowt’f tami err zili lua serp re ceap gic yvow erp!

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.
  • If the persistence library you’re using doesn’t have built in strategies for testing, you may need to delete all persisted data before 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.

Ok vevv egw it szixjaybejs, fkage oja ebkaj furc cach la so rpa xifa bkimq. Kir ujitdis omumpju ot fav ko nezr QuefML, miva i voox ob “Vuim WP: Umzedxuj Viqa Dazdupmedti” wgmmp://ppl.pehpihpunqinz.lah/2926-laim-ql-ajwincuc-sedi-diwtobfozbu. Zio nip ufoy jsufz tu dyiyz xej pua bef uvu zxu zczakird ef gzuc qedaheuw jer ntu potsg vue ssilu is Cressok 1, “Egxivciqion.” ;]

Xer ib ecanlxa us lof pu gund oy RLQaxu luxufobi, zeqa u hook uk “Wihvulj letjuzvovga ub rwe Ihnmaew upoxrxgip” qmwpc://msuy.bopora.dit/wavyipv-soyqaflegco-oq-rlo-edgdeox-orenmxdim/.

Fefign ev tu iz ecuabqf oblalmitv hodin, uv xwu zekz lcebfev, Wxorlov 05, “Lizkemk gpe Reqwudg Kuxis,” moe’cs ruecg lftatijaam maz kehkodp zapi cwah tesueh ab EPU webquds lugbh.

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:

© 2020 Razeware LLC

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.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.