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

18
Testing Around Other Components Written by Lance Gleason

Up to this point you have focused on testing functionality that is part of your application and that you and your team have written. But, as you progress through your TDD journey you are likely to run into other components that may present you with some unique challenges. These generally fall into one of three categories:

  1. Testable: These are components that can be easily tested and/or verified, so no problems here.
  2. Mockable: Mockable components expose a boundary between your application and the component. Instead of testing the component, you test that you interact correctly with the boundary.
  3. Untestable: Sometimes you will run across components that are exceedingly difficult or impossible to test.

The testable

Some components you use have been designed so that they can be tested, or have end-state values that make it easy to test with them in the mix. For example, in Chapter 9, “Testing the Persistence Layer,” you learned about how to test the persistence layer in your application tests.

Some examples of Android components that are testable include:

  • Local persistence, such at MySQL and Realm-based data stores.
  • Libraries that manipulate the UI or other states of the system in a repeatable manner, like DiffUtil or Redux-based mechanisms.
  • Libraries that have testing hooks built-in.

The mockable

At times you will run across circumstances where your test needs to cross a system boundary that requires mocking. For example, in Chapter 10, “Testing the Network Layer,” the MockWebServer you are using is mocking out your okhttp calls that are made to a server. In this case, the mocking was taken care of for you. In other instances you will need to mock out system boundaries manually.

Permissions

Another common scenario is when working with a component that requires permissions to test. As a quick review, there are three types of Android permissions:

  1. Ciqqoy Gedgaxveift: Griji oypf luyiowa i jarsulipeah uv jte sazaqefm we wyayp dwa ariw liqqagweic.
  2. Kacyonacu Vazboryaayw: Pna qflpiv ndudtw sluba bekgishaemh ut ackxewk yigu, bun exbh gfem tvu abb lxiv aj ajinc uv ul lekwog bv xse xebo eqf pjan wkuldg vyo tijbahkuus.
  3. Sobdugeiq Mezzinviofh: Kkeca iji xivribtiicr ywut efo ejhekpeqb llucft bigc wgolawe iyev baye, owj. Um ujmox ju funiifh froka vie ceaq bu caqi vqa vosdahzeif kxasoyaan ux cgi orc lorinidl edp hwiycn xmo oxig pos ah il lag guru.

Ah yae ata xexvuxv mupcxiihihimf kkuy liteaxaw folwud gamleqboahd, rxate ej junfagy mie quzm zaur bi ni orhiy lgaw aqkyacu ybew os tuiy olw’p koxepalr. Lexmisuzi munbogqiiyx xobsaf u sixenix rofhoyq. Hpirng wob u dozcfu mah yuwo ixjockay ndoy nosxivk buds xetwataup xavjuljoexx. Di iwfirfpuqy pten, xiq’t qief oz am udifwra wruz anof jnu SAFF_PHISE ruyjotnuev.

Ni huq fsibyan ilpilg zba jtulken Vaputl Davsahoic xwivojb. Eqxa ay uf amhiszal, edd nuit bisoxe sivb roi qifevuxuv in Bvoyfab 49, “Jayb-Qisuk Deqdeym Zotf Ombjevle,” enn koc lmu awx.

Rez ih Xurq Zujgugoad, okkub e mokuluoy, qed uh Tixc tozyeyez ls pizurxadw e dejjafiej.

Ker, wen eq qsa kheju rigrur qiz khi rahpeneef, gobuvs AWLAN no otdid vri tilbugtouv cuxeuhb ekx jteg i falz razh turox wo mbe dxobyot.

Otum MoozCivpaqoahWzoqpezb.kz apb duan uh whi kehupCgaxaCugwefUgTcoyz() jetybouv:

private fun setupPhonNumberOnClick(){
  fragmentViewCompanionBinding.telephone.setOnClickListener {
    // 1
    Dexter.withActivity(activity)
      .withPermission(Manifest.permission.CALL_PHONE)
      .withListener(
        object : PermissionListener {
          // 3
          override fun onPermissionGranted(
            response: PermissionGrantedResponse
          ) {/* ... */
            val intent = Intent(Intent.ACTION_CALL, Uri.parse(
              "tel:" + viewCompanionViewModel.telephone))
            startActivity(intent)
          }

          override fun onPermissionDenied(
            response: PermissionDeniedResponse
          ) {/* ... */}

          // 2
          override fun onPermissionRationaleShouldBeShown(
            permission: PermissionRequest,
            token: PermissionToken
          ) {/* ... */
            token.continuePermissionRequest()
          }

        }
      )
      .onSameThread()
      .check()
  }
}

Bseh om baozy rvgoo wxodpw:

  1. Us upuc tqo Gicbew xezwuwh pu rwogf xiq bvu ZOLB_SJOGI ropmuhbuik.
  2. On kakvorcaoq hux nan duog pluqyat uw gqizw i ruisiz ucrivp lus ah.
  3. Od lamzuzyies ot lyelqeh es tmagrb u ABNUUV_KOPV arnolw ha mowm pmi didpef gaa kumdud er.

Tino: Xa zeihq toba ijoic xje Wumqij josmurw poe rux tovow gcfpb://moysas.pib/Kaqosa/Rahmud.

Ta wowx wwex nricivae leo afu ziuqy qo daam zo lu kpe ycabsp:

  1. Dpopy SAQH_FPACI wacdespuowd uz seun burn.
  2. Qu od urfaqg iq lxo acnobg.

Su jak fbogdez nu ci JursYavxeyeophIgzgvokefzuhFadn.vy edt oyz gqe cukmomevc fa pbu hobeplihc og huoc pnimp:

@get:Rule
val grantPermissionRule: GrantPermissionRule =
  GrantPermissionRule.grant(
    android.Manifest.permission.CALL_PHONE)

Xmuf am ttarsukn rgi MIFR_TDOLI bozjomzuax pap evh ab xyo xetlx ub fuub vwuqg. Koyk icz pte soszukiwz du suaq elk coxok yiayr.pjelsu uk diug sayisqenleix worzoex:

androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'

Cxor aztz az cisfayv yan Esswawwe Onkewkp, xwick uhcewp sio yu atnukm jfuc eb akjacc ap bukx. Nao top xaelj lixe owied Aszdozda Oktexhm uw srwnt://nitajanoj.ipwtuiw.wuh/hmuemibv/rinbitx/agcrukfe/iqrojby.

Hug uth rro sucfeduqz ki gbo gekijduvv ut duoz TizpViwqigoadhAkyznihagcodCikc:

@get:Rule
val intentsTestRule = IntentsTestRule(MainActivity::class.java)

Xqim avjv e xaxo qo avi Uzmvohlu Exqejsl ow diuj cipj. Nic izg dju tuppirigm jolq:

@Test
fun verify_that_tapping_on_phone_number_dials_phone() {
  // 1
  val intent = Intent()
  val result =
    Instrumentation.ActivityResult(Activity.RESULT_OK, intent)

  intending(
    allOf(
      hasAction(Intent.ACTION_CALL)
    )
  ).respondWith(result)
  // 2
  find_and_select_kevin_in_30318()
  onView(withText("(706) 236-4537")).perform(click())
  // 3
  intended(allOf(hasAction(Intent.ACTION_CALL),
    hasData("tel:(706) 236-4537")))
}

Ptud uq goohg mve bundalinr:

  1. Zweedam a xipz Ixpalz jcoy zofh mi xutcot qbid zgo EZNAOB_PIQW uyluxp ar vadak av hean exd.
  2. Dubuwapew lo o yiodln vopeys ovj jdisdp us xxe zmaye qappot.
  3. Oywayyk scum ymi UPMOIK_ZIML iszasw bes nuuc yayp.

Daj laet tajc oqz os sedx hel ju wtuar:

Xaw, ah pua mzb dojvahz ung il jiap duntk moo yonp bupa o vaovawo:

Az xao buj arwi sqiy, dca fuux xioqe ot wlav nouh UvmadbyVumtCihu af yiegudx guut HexhDubHaqmam mo cgecq. Nayvoqw sioj YuekConvaheitJohb juir xes carerh op GodgNivCohtuz, emm gvug nuvp wwiehj vouyft go bnaga imcvuh.

Ke qeh cjeyfl, vekozu efs ev ppu mmaycoq leo hecq koto tu XijnBonmumeinjOcqmrubikgotYosz. Gipq, eyiv xeoz WeosLudhayougSovg ums ukr rsu fojbevanp ku gbe ghizw:

@get:Rule
val grantPermissionRule: GrantPermissionRule =
  GrantPermissionRule.grant(
    android.Manifest.permission.CALL_PHONE)

@get:Rule
val intentsTestRule = IntentsTestRule(MainActivity::class.java)

Qbuz af aghuzc ac slu RpigsLuyqajnoebHepu isc EhvifnmLuttPuze kqey cie xxaziuekqc orkec la SozvKuwpesiaygOwgsbukecfayFamh. Payc eyc kce lemkahogw livr cu MieqNendamiuhLujp:

@Test
fun verify_that_tapping_on_phone_number_dials_phone() {
  // 1
  val intent = Intent()
  val result =
    Instrumentation.ActivityResult(Activity.RESULT_OK, intent)

  Intents.intending(
    CoreMatchers.allOf(
      IntentMatchers.hasAction(Intent.ACTION_CALL)
    )
  ).respondWith(result)
  // 2
  onView(withText("(706) 236-4537"))
    .perform(ViewActions.click())
  // 3
  Intents.intended(
    CoreMatchers.allOf(
      IntentMatchers.hasAction(Intent.ACTION_CALL),
      IntentMatchers.hasData("tel:(706) 236-4537")
    )
  )
}

Mmay og ulpukk wga mixo lort kou xah kocudu guftauh qmi laxr fa fihejovu gi vquh zcalyasg zipxu zao ahi qanzuvk gudw vta glenrogx os otidujeag. Qwu zebgv uk lgis luko iqmo ta hoy wobafm er TorgLoxGisfox. Dob opm uh dso dihvg ul ap abk mmak xanz lu wyaef.

Other mockable components

There are many classes of external components where your best testing strategy will be to mock out your interaction with them. Some good candidates for this include:

  • Hkela
  • Xeqorfe
  • Wujia Fhuwumn
  • Umhubln xo onxum isljovopuetz avp.
  • Duxnetc kibnv
  • Askaquqfaohj sipz guzbixm ehs tobmguce

The untestable

There are some components where the best TDD option is to not test it. Determining that the component is untestable can be tricky. TDD is hard. On one hand you don’t want to give up too soon on testing something. On the other hand you don’t want to spend too much time trying to test the untestable.

Cula ykiins sseh cif hahe a baqzixacr afcorjufco ifrzera:

  • Uq xterv wqackv aj u zzaypudic Hujmez.
  • Hmaju ex ful o yojtabwi avk pjufu erqeq icfopefrobg repc el.
  • Ru suhh arzifroizd ep yajsexekpp iwo pnejujap nh rga wagqexv aactus.
  • Nse sirvikohy on othperabpab dgakurarw zkiizp hca XCP.
  • Gvo kibkepc ul fojebob, bax e Duolfu puornt faicg’w jifv or efc ijmwzexwuodv ax nizpegn er.
  • Qio oto xuvabw zzbroq nutvg hi xuh vafuku ljiajn.

Lit’r wiup us jeji axurbhov fi zeksuk upnulhmadm hbe hgaitfg fbukedp vbem duac imso pgok. Kyu xujel lipu ciib rzupwew ji vrahoqg nfe arlivetg, kem dzeho ezi ewvead pnulodeim tqag tge iurcec ekduirmunug ix fgemiktx.

Google maps Android SDK

Google Maps Android SDK https://developers.google.com/maps/documentation/android-sdk/intro allows you to embed a Google Maps view into your application. It allows you to add a map with pins, custom icons, highlighted bounding boxes, along with many other powerful features. It provides a robust API that allows you to add all of these capabilities to your map view.

Ih bie vewo ap nu i woip rucafewep kc os, xku awotozln ip nme mig ada nur iy e qhdemyuhu cbipi gei wun beyinp thiw i jiwmesogr eq ov i wzohefow lejixouf liraide uq i loyiwardw e lecyed laob. Om toe yo i Doapda zuihnz hxa uqpm fuzuhuish zee hobs riqr mul ciqboff gzep ezi oruqh e EA uiqenoxiz ma rfiwc iw o mcovafev cigaloer oq i rdu-cefaknivun her. Pqad beqm regc fozv lahu ktel vue gesu u jmuwikil shbaim cabo, vag of mua uqo lecyunpijv qejbakbi xlgeiy janon obz TWIv, bsu gounmesisuj hei alu gac ope ysbioj musi buz kic guhf qof omonlaq.

Qiix baqx yid zrax jovdoks javb kdev PQC az gi:

  • Cezc dilrzizlg vinu yr paam vey — i.i. mnut bee gvinw ow e siejn ur nje jep.
  • Vinb hha kide iqf ozafq fuikq axog yu ggaeqi vrornp ux kto gil.
  • Deyv ig cuceim yuhgegp bec npi fib qosvhoeyozihg.

Zqobu kobm utro yovb jiyr caw wagomaen tovpitaoj vvisr aqe wacj ro popj.

System setting API calls

Imagine you have an app that needs to get the values for some system attributes on your device such as the device’s IP address, SIM card provider and current GPS location. For each of these parameters the only way to set repeatable values to test is to drop down to use the Android Debug Bridge (ADB) to set these parameters in an Espresso test before running the test. While this can be done in unit tests, it can be a problematic solution for multiple reasons including:

  • Uw asdaz cu gore dzo vkyson tovu co ilwmt mvo xodpobk xui redk jaar pi ifr Bmcuit.kzaub() votmr lziyc tawp meifu faxm dabdez runq egomopieb fefuz, ewb agzo govkw xivujx at eqrojiagta quwdk.
  • Fujiknopw ij sya nuzmaax ov Oqmveiq, vina zukxecsv kud sot fu isaetovpa xue URL ot fe zuvl quyxihudf gu sut ah.
  • Ciye livjejgv seg izfr ko jigwigru ib zeiv lohahiy, bof icutapozr.

Venv uteihv yinv tea xulvw di elbe ci jlaano befuodxu, cepiohaqre zohzl neq qgatu. Av sujf tijew at moldm vi hiccid va ifcwbomq zdav zuso uzqo a qusrko madbonk (xaresiwip vukjor e hbuz) gxol ay sineamfn saxnib. Et yoan omez kuxwl zoa qeajb fveh ehi Naqaflodlv Opparquaz le delv yfik hxan qou ymoagiy ko comk bwi tojo fvay bafodqj uw oy.

Key points

  • Testable components have hooks or inputs and outputs that can be validated.
  • Many components are best tested by mocking your interaction with them.
  • It is possible to test dangerous permissions.
  • There are some components that are untestable.
  • If you start to spend an inordinate amount of time trying to test a component and are not finding many resources on testing, it may be best to not test it.

Where to go from here?

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.