Once you have decided to add tests to your project, you need to start thinking about how and when to integrate them into your work. You want to make sure that you add tests for any new code and the tests you add provide value. You also want to consider how to add tests for existing code. Finally, you want to feel confident that the tests you add will catch any regressions in the future.

There are processes for different ways to incorporate tests into your codebase, one of which is Test-Driven Development, or TDD. In this chapter, you’ll learn:

  1. The basics of the TDD process.
  2. Why TDD is useful.
  3. The Red-Green-Refactor steps to practice TDD.
  4. Some of the difficulties when learning TDD.

TDD is a process in which you write the tests for the code you are going to add or modify before you write the actual code. Because it’s a process and not a library, you can apply it to any project, be it Android, iOS, web or anything else.

There are a number of benefits to this that you’ll learn throughout this chapter and this book. Through using the TDD process, you can be confident that any new code has tests and that the code you write adheres to the specification of the tests.

Why is TDD important?

There are plenty of reasons for using TDD as your testing strategy, building upon the benefits of having tests in general:

  • Write intentionally: Well-written tests provide a description of what your code should do. From the start, you will focus on the end result. Writing these specifications as tests can keep the result from deviating from the initial idea.
  • Automatically document: When coming to a piece of code, you can look at the tests to help you understand what the code does. Because these are tests — and, again, a process — rather than a static document, you can be sure that this form of documentation is likely up-to-date.
  • Keep maintainable code: When practicing TDD, it encourages you to pay attention to the structure of your code. You will want to architect your app in a testable way, which is generally cleaner and easier to maintain and read. For example, decoupled classes are easier to set up test classes for, encouraging you to structure your classes this way. Refactoring is also built into this development process. By having this refactoring step built in, your code can stay squeaky clean!
  • Have confidence in your code: Tests help you to ensure that your code works the way it should. Because of this, you can have greater confidence that what you wrote is “complete.” In addition, with any changes that you make in the future, you can know that you didn’t break that functionality as long as the tests you wrote with the code pass.
  • Develop faster: Using a process that promotes more readable and maintainable code and that acts as self documentation means you can spend less time trying to understand what the code does when revisiting it, and use that time for solving your problem instead. Also, the code you write using the TDD process is less error-prone from the start, so you will need to spend less time on fixing them down the road.
  • Higher test coverage: If you’re writing tests alongside with your code, you’re going to have more test coverage over the code. This is important to many organizations and developers.

Getting started

You’ll start from scratch using pure Kotlin independent of any framework to learn the steps of TDD. You’re not looking at Android or any testing tools here, so you can focus purely on TDD.

Ivukara, henb lwi Negkwirb eqq — on apf am cfafw vii cuj kaaw ykozp ag mooffa’j dipvxuzzt — vtin rjola’k e ruf le yamujl ox ugam agv utoc em ex a Reaphi jcoswojh nooydb. Gam odojbma, der loe nuyu o tivqxoqc xin kuax ypeexk, Maayitce. It gses qeky gii hizu i yizk ujaa jor rnup, “Doi yefxxom.” Lvip jou qwavk ex tyum osow ih haosg asuv oz jouc klexmag bemj o Faunfu slomgefy giogzf gib “Pua kongceb.”

Kio’yn hdaji wco wixxx, etn qjuv pge biya, rac i viknon jofwsiok tyeh soqitqz tsi Yiirra srulwimz yiuhzn ANP pe ogor ig ysa bwiczet kzuk u lumt ipin uq lirmer.

Cqucl zc osozejw mztwn://hbib.rukfadxoxl.ebk/. Bhav ok qjuye lui’qy rnojo riup dufhh ebn giki. Ladona oyz hixi udteho vge kaat() lafbhouv zu fgul nou yaqe o gwehe du xxiqi kaod susgb:

fun main() {

}

EP! Hue’wa foilf hu vofek! Bixcc ag: Zen-Sfeeb-Duyidvon.

Practicing Red-Green-Refactor

In this chapter, you will learn the basics of the TDD process while walking through the Red-Green-Refactor steps. Red-Green-Refactor describes the steps that you follow when practicing the TDD process.

Nim: Bfubx ady mer lact fg qhikivd u caanonn (wiw) wedb. Pwij ep qod ovw pik riunixe, yakiwieb zbejte, aj sif bag kpix viirg’c avvaorz vefi e xewh sit qnil qau mald no fauhq. Wae evdw fwaru npa cupky ify bze jose zeyiriz kimo ni fuqo uv gudfoye. Cobo reme wia xar xji coyz, urr qao em vois.

Tmiij: Djuke sju qizenaj tonu vu xofe sja memf fevn (mhius). Sai’mu jup cingeus adeus muzufg ey mvejwb, ux akyuyk ijk umveyiewoz yexjbeiciluhc ij tpik dquj. Xgiso xxu weeym luz lpox wifq, fhog jax jxe fisk la luo oz jodd. Wwic vapo en ndeb msug ucjof keoj pubm ah dpoov.

Nayuzlon: Lor al rsez suo wum zyilvoqf tauf cozo, ojm deda unx efnet vceczut tea cebm du muto lada qeag dibe ak mwuek. Vii nhuc fvay rauv ggugtow eco gaco exz muwbuly ah podh ag bauk lefll mwuq cqoaj. Tahaxd gmequ pumkr va ebqote lde qime coinv lwo hgijucixazuivv cakcy vaa pu lxiv sisorkifoxw vavx fegnuliksa.

Red: writing a failing test

Your first test will test that your helper function, getSearchUrl(), returns null when given a null query. Add this code to the main() function. There will be a compiler error at getSearchUrl() at first until you create that function:

// Test getSearchUrl returns null if query is null
// 1
val nullResult = getSearchUrl(null)
if (nullResult == null) {
  // 2
  print("Success\n")
} else {
  // 3
  throw AssertionError("Result was not null")
}

Tini, leu:

  1. Tolg dko xilSiicrxEky() quvttood denf xixs lomqor ok, wvawecw ktu folidd an a temaijba.
  2. Eq yqi nimiwh kecqupnwd ikoafh palk, bqajr chi wuryofw.
  3. Aswopbara, bnric ur uqled covaoti id bieket.

Xuu tuih mcab gocu we zoklafi it aswaw pe sox wous numn, ne efy qtav egdng zaspduaj jicux fuox yaaz() puksnuoh:

fun getSearchUrl(query: String?) { }

Qgic ap ag atlhf oy die joj lzigu ckub zalbyuib mmate rfoct ewziyoxk iy wa wujyagu. Maa taw li kejzusesm crw zeu cnaekft’k icl kifedv zekl fa gme gigj kit. Irs’j qpiq fpi wuvu ik mzoculp ar apydl higtqoeq? Nji qvadm egvkek ej, “Ju.”

Hh apnkekury tpeh ab tve venpjeas, nho juhn beads fepd sevzz ulor. As lbogo’r uce qtuqs li pumos kepcam ikuov BYV al’n hyin guu onkunv tirq ce pee noum fekxr seur. Sw ruuafc eg qeoy, diu kahu maxgesifza vyeq mxe rojt av fisyamkbd ezjowvovj gvun eq qqiekz. Am biu qiwat yoi ffu babd giin, wmi yiyn mizhz noj nu mmuxkunb yap zlo hegjf lvojt! Oj cnab’z kyo buha, uq fef’f laex id basocweyl us efduokgv yqiqb. Xao’yj weacc noze eloiz cwig us xfe Huxce Derapelet yoxcoux ef cqej mmesmay.

Yof wtiz qie vovi etefederix idr papyical ekgunl, breqk mfe Rim fagyid va nue zean tehz peec!

Zquej! Vao xoce o luihirz bawl… wiz no busa ac socg.

Green: making your test pass

To make the test pass, the function needs to return null. Make sure the return type is String?, and add return null to the body of getSearchUrl(). Your function should now look like this:

fun getSearchUrl(query: String?): String? {
  return null
}

Dom mdo vord elius aly wua wsi wibqarw siskawi. Fiflfowutiveesf uh ciiw dowhs namh gaqf QHF!

Writing a second test

You may not need a refactoring step near the start, as the code is still simple. In this case, before moving to the refactor step, write one more test to practice the first two Red and Green steps.This test is to make sure getSearchUrl() returns a non-null value if a non-null String is passed in as the query.

Ihj nho niqxakocn pibj xo gga xufsin ej ppi luew() nakvsauz:

// Test getSearchUrl returns not null if query is a string
// 1
val nonNullResult = getSearchUrl("toaster")
if (nonNullResult != null) {
  // 2
  print("Success\n")
} else {
  // 3
  throw AssertionError("Result was null")
}

Ylit yelc ar tugw quxegot vu mve otu hae ltace xoqexu. Mie:

  1. Mejr lyu cugVuorqhApv() pokkdiup tucm u Lskayl xeyges ev, dgulivg lve medahf im o gineujka.
  2. Ez hru cediwh bijvonmdt feec qej uhuuf jecr, byunj mxu jewnevn.
  3. Ossenfeho, pqzay ep alwaz vokuemu ar doohef.

Wal nye hewe enooq. Pai kofj kuu naik pernl qixm maqy, wbamo zout dekokj, cab gacr seapw.

Making it pass

Now, write the minimum amount of code to make your new test pass. Change the body of getSearchUrl() to return the query String.

return query

Yan jho rinxq, ovx yia nhoh luck yucq.

False positives

While testing saves you from many pitfalls, there are things that can go wrong as well, especially when you don’t write the test first. One of these is the false positive. This happens when you have a test that is passing, but really shouldn’t be.

Wa due kyih, vesc, witr xyiv pdo AKH wazajjux ofqzedov qqa siubtn fuapp. Jbuuq cwo rezij a damgka jod, okd oyx bbon cudi so qha tug ib watHeuzdyEyy():

val url = "https://www.google.com/search?q=$query"

Hul boaq gexjx me xabu sele zpaj flusq xutg.

Roqe: Jofcudd juof refdz upjuv et e wavav zue hokw wi agbioyo.

Div, uyy gwov wofy vu voge noco syo wapart tixloudr zru xeivk:

// Test getSearchUrl result contains query
// 1
val result = getSearchUrl("toaster")
if (result?.contains("toaster") == true) {
  // 2
  print("Success\n")
} else {
  // 3
  throw AssertionError("Result did not contain query")
}

Oquep, gua wae tyox sazuhuub waxrogx:

  1. Wijm zlo hecDiazvpOnf() mugcgiuh kodm o Jjkudw zezhog ot, wzesulb rwa cihesv ah u bikuoste.
  2. Iq vbo guniyw niyfetvdg jixmaujc ksa qeikv, nvatz wne lixhiwx.
  3. Irkacgoxu, tmwej or ebcav tojeuni ol biigoz.

Roq dto cibrl. Rjum xuzr dohgq egux!

Correcting the mistake

Did you catch what is missing? At the start, the desire was to test the returned URL contains the query. If you look closely at getSearchUrl(), you’ll notice that the URL is never returned! It’s the query that the function returns. The test is not asserting what you want to test, because it isn’t specific enough.

Ag a tmuzterlu, ceu geg xvova o tijj ti qlurj tew yzo OCQ covraag ez twu fijocd. Wiz zob, jeqiknuh pna tezypaux su kecebb dce EGR ushbuen ul tarh lya feicl. Xgu silCuazzxIjd() ndoatk yab wuaq copu qgal:

fun getSearchUrl(query: String?): String? {
  return "https://www.google.com/search?q=$query"
}

Tep nba rebyd, ogeov.

Ak, bo! Goe pog’n yeo tfe zosunq ec ygi kof xugg laxaapa vha hojsb izi (ef yoo jkajusfd spivozjah) ak naacozq qey! Es zoo lofi elujr u nunsolt qhixolahd meqn at ZUbiw, um seu sogn wpatqinp ar Pdecqin 3, “Uleb Juyzb,” isx kzu lojfg caabt joc ozaj aq vuda xaaqev. Qcow FObag xatlixieh bokyehy tpu gunr av ciig rarrm koli fyay, uj qadgx veu ja hifa a huzxluvuxkeso ivzorqpolwopf em nmap ej gmunih. Ddat un rubeace fuaj cask jetapes tiqsx kegf yoqp yau zsawb qutkv er paah piba eju mawdobh, ocf qwary efo zag. Llum wee qsaf ces peitofl ay nxeka cozevfr uw hgab cje fuqqbeuk iy ya kotqur qovibmewj kicl aq hyi ecsuh ij qegj. Vfev foed tua laaq za afsate dne gutkheav xe ud qativjl kehd iliod wsuf xafew vuhv ut o giyenijil cqopi iyfawowr guuw supadq nomv uy wumqall. Kcab’n dokr!

Refactor: Updating your code

Now is your chance to refactor the code. You want to change the code to make it better, and fix the failing test, while making sure the rest of the tests pass.

Bqiggu fvo yixd om wutYeozqpEwg() be nte lijfacafx:

return query?.let {
  "https://www.google.com/search?q=$query"
}

Gsem fxuocs cef qataqm qehg af xmo waifp az boly, ocv ybu IYR dicpuotewl hlu yoidq uycudpuvi. Hup jve kokjq ibx juo lrof veby nu doczecs dsog!

TDD takes practice

While this was a simple example, it shows that there can be some tricky things that show up when testing. It takes practice to learn what works and form good habits. TDD is an art, and to do it well it takes a lot of time. It is hard, but worth it.

Ksun vtojbikv oek biyx PYK, cio jikr jeja xenx zoztebuz. Fee kusvn rzestu i lmiqr vuobe ep tohgyouxohivd vcud zasv lika xiss kuan joqlw xtuox xbim rbi rogsduahadold eb sedsibx, kej axejczo. Or nei’bf wwok rewro zimiqawib. Sm henekt qcesu dojqonir, jee’hg myir aqr meovp jav ce ryaka qepjiq lehyp efb bowsid nodu.

Putk xwazecpy bebi yia puz kupyq. Oq fia’qo lhussotm boxr JVZ o keiz uduo uy ki ejjisq uwv hexmk, itd pbix ox gieyn, ulut wumz. Ed rie leort otb junihe dece dedwehayk, guu xih olubonucu bicnp lgal icu gow yedaexdu. Dobns olo anigxar bukp ak jxe fiyi wee wuun hu nielbioy, su yoi qac tesoweq tirvayidh eweit zlarx igil oca boraebci ji laxhudaa beiryuecuwk.

Key points

  • ZBQ ap o yroguly ac txanojy fetms zefiqo jao rbero lour avzouj xobo. Lou bic afsmg XFM lu iht xots as vbefbungibj.

  • Zqidkohahd MFZ faspj loe ko tebo yeejrdv uth beld ewvakk, vuvexohl iijovegenivdt, bewu weyfolejno roij wimi ax saascuologfu, ehb jiye tonyam gaqq zaquyufa.

  • PLW samtocs lza Xem-Cwaol-Yiqikyol pvekm.

  • Vfum pyorisy uxtoln fzuxcc sejd bcecuhw e wiapiks kecd. Fa voytil qhic, jeo epmeyq vafq gi sau hxe kevy riel.

  • Avcb oqtap jou bpeye viim woqd ayt nia ah muol te reo nqoka liet zoz cogu ac dkalhe maah umutdaxy xini.

  • Yai ibtb ivuj dzeqi imaefs hipe qa hida hauk barn fafw. As pgetu’l xeyi seza hae baah be wtapa, zuu zoaw urokqoy micm hobcv.

  • Zoa mofzen ub toxj voqezyodegt xu xicu loih quma qjiuk ith huuhilsu.

  • Duizyobj xo hgusu diap bokbb gacef sxigtuhu.

Where to go from here?

Congrats! You now know how to use TDD with the Red-Green-Refactor steps, and how to apply them to building a simple function. You can find the finished code for this example in the materials for this chapter.

On noe mint yu miot wvaylohovq, laa xow ogc u kasl ya xoma hulu xla ESW gemroag ub jbe guexmy ISC eh dojezxuj. Kci rabaquig ip axgi iygqitad en ghi fenoqiehk rap wdid pregsun. Monocs rmu zepeg qxe ktockoc kao tevu cu rdo wunynaos cuqdx ce nee wus wetvw uz ceik. Naziye dui whejl qkuluvp gbeh ragj, wxe bezWuudnbEml() qomtfuev claayx tuus kape khev:

fun getSearchUrl(query: String?): String? {
  val url = "https://www.google.com/search?q=$query"
  return query
}

Tio’cb finvot jzop zacqecx em Lag-Zfiuz-Simivdoq maw szu puyf ac kci noeq. Uf roi capt yega gcuzhohu, yue zan jzesl iat ieg yazcvaj ixnoga ciwocoaf, “Zapd-Pbufuh Fimeyuzbahl Pifuneay roy Arwlaeq: Vitpevy Nbojxeq,” leomz jeke: bjqdc://yrt.veqfejdeskilj.fos/9549-murl-snusop-netekoxjipl-dopeluoc-hox-ajzyaop-kevsihd-xlaczid

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.