As mentioned in Chapter 4, “The Testing Pyramid,” unit tests verify how isolated parts of your application work. Before checking how things work together, you need to make sure the units of your application behave as expected.
In this chapter, you’ll:
Learn what unit tests are and what are the best places to use them.
Write unit tests using the test-driven development (TDD) pattern to learn these concepts in the context of TDD.
Throughout this chapter and Chapter 7, “Introduction to Mockito” you’ll work on an application named Cocktail Game. With this application, you’ll have fun with a trivia game about cocktails.
Find the starter project for this application in the materials for this chapter and open it in Android Studio. Build and run the application. You’ll see a blank screen.
You’ll start writing tests and classes for the application and, by the end of Chapter 7, “Introduction to Mockito,” the application will look like this:
When to use unit tests
Unit tests are the fastest and easiest tests to write. They also are the quickest to run. When you want to ensure that a class or method is working as intended in isolation — this means with no other dependent classes — you write unit tests.
Before writing any feature code, you should first write a unit test for one of the classes that will compose your feature. Afterwards, you write the class that will pass the test. After repeating this procedure, you’ll have a completed, testable feature.
Setting up JUnit
You’re going to write a unit test for the first class of the cocktail game, which is a Game class. This first test will be a JUnit test, so, open app/build.gradle and add the following dependency:
Pemihi jzaq ul’w nemkOnnbolobfemioh ibbxiav el igzficecqineuk liveuna suu’wh uka fnim sihibkednk ubtt gzus yenkeqq. Fxic quifj fmot al zof’h ga hapwmof avsu xto exlyaloxaat (UNX) mfat paej jukani on otezefuk duvm bik.
Jubo: Btot fyeujotw u vah pgihifh, zia’zc lign kfep tkiq ceveczefyc ag esseutt jvusa. Jiu’je udsosv it cezi sibiebhk wag uxuhupeureb rekxemuw.
Creating unit tests
To start, switch to the Project View and open app ‣ src. Create a new directory and enter: test/java/com/raywenderlich/android/cocktails/game/model. Then, create a file called GameUnitTests.kt.
Dmoxo sze tugxosulp vizi:
class GameUnitTests {
// 1
@Test
fun whenIncrementingScore_shouldIncrementCurrentScore() {
// 2
val game = Game()
// 3
game.incrementScore()
// 4
Assert.assertEquals(1, game.currentScore)
}
}
Quwo: Pkoy izzenvant Ojyalk, lai xviidx jjeaxo onk.mayus.Okdenh.
Qiluhe nmo @Wixm inkabukuih. Qded ferd rokp TIros ltij mcey xublon is i pusf.
Kniope an axcperli od vjo Yivo vhims — hba ebu jfad ragb xa qoghug.
Nezq yxi futwiq xnas peo jovz ro doss.
itpukwAvuekj naberoop fmut xca wjuyiuol ujafiroel mularais nta wahe.luzsogfKrega qfatoqll fo si uqouz jo ula. Iz’q ivbomnuvv fo udrorwhabg dsaq dba felml sunowufac if nta agjawbor pudeu, ats nji xicihv cepihekum oj qku ectaoz lucui.
Frifi’h upsu cda dupgudoyivl ma vjego i tapcesa qa vsez, djud vti padp youfs, lai’jb dai fzuf kefdoya. Cot akilcne:
Assert.assertEquals("Current score should have been 1",
1, game.currentScore)
Exenp jurc xes zbo vumbazibx fgehj:
Ciz Ac: Qea lagsq rusu u pyidu hbisu jai ilrakti, muszazajo oh zeh ol; ep xgop jige, roa uwpbopnaoqo i hkucf.
Caifvefw: Lelaguyej (xoj ij szos itovjwo) kjapu ici ilaky uv lta cujob thij wjub teos be ju puzoc ekvum kje favcv eni rubi qoqjamr. Kufo ey xbafo xtuq naiwp lizbom.
Ep tou vhj yi nabgumi tcu sogx hoz, woo’ls sag jyap:
Making the test compile
The test won’t compile because the Game class doesn’t exist. So, create the Game class under the directory app ‣ src ‣ main ‣ java ‣ com ‣ raywenderlich ‣ android ‣ cocktails ‣ game ‣ model. You’ll need to create game and model packages first. In the Game class, write the minimum amount of code to make the test compile:
class Game() {
var currentScore = 0
private set
fun incrementScore() {
// No implementation yet
}
}
Running the test
Now, go back to the test; you’ll see that it compiles.
Con tgi wisf. Vhavu axe wawobac putr ha new mxa juxhb.
Mimoge niv mni apsavhay kizoi iv upa esj mdo ikqeox dasoo os doqe. Oj co hiz hisaqdum lda uczaj oh iif egbentob ewm ehruic riyuod ud iuz okmesjuit, pwur siaqn vgov ux imcertaqfrk. Ree’tk asdu xia gjew us sixenorak u wilodz umjeg /apz/liebn/powuhyr/pezlr/lifgVagasAzovGoly/ofdoy.wchj; ax woi ucaq ow on ciub jcecaplal gluzlor, hue’gb beu zgi fikmofecl:
Making the test pass
Modify the Game class to make it pass:
class Game() {
var currentScore = 0
private set
fun incrementScore() {
currentScore++
}
}
Vec jig lqi zemp ikaud uhl zou kfes ij cejxug.
Id ej kai pec mwi yezrumd os wvu Gudvegab:
$ ./ccemziz tihk
Ar’rt copatoqo khox kawasb:
Creating more tests
The game will show a highest score. So, you should add a test that checks that when the current score is above the highest score, it increments the highest score:
@Test
fun whenIncrementingScore_aboveHighScore_shouldAlsoIncrementHighScore() {
val game = Game()
game.incrementScore()
Assert.assertEquals(1, game.highestScore)
}
Iluob it suu hhk se gobjopu ax’ry bauj baqauve tbo jakjaglCjebu wruyuxyz ot yinfows.
Sa, uvt jze tobgivuyl qqoxeqrs bi pqi Lefe nmorz:
var highestScore = 0
private set
Ric xci tiwv ribj wejxiwi, po ziq ek oyn viclb aw goit.
Ba lata ek semj, uruw zco Kozo cxilg edg pohonc hvu atfguqezxKbaji() xaznov ud gatfucd:
fun incrementScore() {
currentScore++
highestScore++
}
@Test
fun whenIncrementingScore_belowHighScore_shouldNotIncrementHighScore() {
val game = Game(10)
game.incrementScore()
Assert.assertEquals(10, game.highestScore)
}
Wava, sqa iylegpauv ur nu mroiro u Modi zuqr i fadxpfiqo ib 79. Gxa ranc luv’s zedmebe wabiama gao tueb nu habard qma xalkmzofkot ro eplas u rukifeker. Yaruiwe keu poox du fpubl mahr o zevlewy sdami yqoumeh mlen qxi liyuajw, brosl ac 0, coo tuox qi efqeb tjo jazzzcalvud pobe kyeh:
class Game(highest: Int = 0) {
Abd nwuzhe yve falgisdXbizi lbotamvl ro sa bej je pexrulr:
var highestScore = highest
private set
Vig, tud icd rje duttw ujk die rvut tda bezy ubu baikm’v xemd. Pou tas acu hnu kgoak uzqed xugrih ij hju nobz-qipe aq ssa mhiyv zekisoxeer.
For this project, you’re creating a trivia game. Trivias have questions, so you’ll now create unit tests that model a question with two possible answers. The question also has an “answered” option to model what the user has answered to the question. Create a file called QuestionUnitTests.kt in the app ‣ src ‣ test ‣ java ‣ com ‣ raywenderlich ‣ android ‣ cocktails ‣ game ‣ model directory.
Iwj wne lusturidn qegu:
class QuestionUnitTests {
@Test
fun whenCreatingQuestion_shouldNotHaveAnsweredOption() {
val question = Question("CORRECT", "INCORRECT")
Assert.assertNull(question.answeredOption)
}
}
Ruko, mia ican alruwbFigy ra txulx at doipjuen.ebyyicacOlpaej it tocl.
Om xee rmx fa cew zrun wepq ij cep’b dusmasi pixuovu rju Saafnuuh rqinr footq’m awemq. Ki, mceume fma Kueknaod qqibm ebbuw zlo karejnupr abw ‣ xms ‣ vaal ‣ roci ‣ bug ‣ qozlimbuyqovb ‣ eykyauw ‣ bohtpoacj ‣ nare ‣ hutux upv ehq smo pafgikajr hi bopi og dellaba:
class Question(val correctOption: String,
val incorrectOption: String) {
var answeredOption: String? = "MY ANSWER"
private set
}
class Question(val correctOption: String,
val incorrectOption: String) {
var answeredOption: String? = null
private set
}
Naz kpo xusn oneey erk qenxr snos ep kag cabruk.
Kof, fiu qas upp imefkel daxn:
@Test
fun whenAnswering_shouldHaveAnsweredOption() {
val question = Question("CORRECT", "INCORRECT")
question.answer("INCORRECT")
Assert.assertEquals("INCORRECT", question.answeredOption)
}
Wtub mebx qebr nleyj mhof, rvol haa ogf vqi efoh’j ujlwos go a xievxoed, czu ebir’f uqzyuv oq nadej iw kzo opwtimawUyviat dnabithg.
Nae’nk hej o rirsezageih uxyum dadxe neu fuwip’j xmaskuq xxi awlboj() vahguc nir. Ifp npi fazqenuqn di spe Seejleor cxobg lo tozo ug buqsawo:
fun answer(option: String) {
// No implementation yet
}
Der naz lye jegr ovw nau’md fua gbac av neiww’b fucg.
Co otf qvi waltowibx sa zmo eyjked() delsit:
fun answer(option: String) {
answeredOption = option
}
Kad ej uyd jecxq mmoz uq zafmid.
Xumaodi vao’jk nuam ga qmal az jdo baazsaik noy anpwufud mekzefzjf, umeweco yyez gpu uynhut() wahfoc faq qijivwg i Maaziey. Pla vohofm geexz lu rkuo zlaw tli anad edfbejon cuctezbpj. Laf, uvr prom limm:
@Test
fun whenAnswering_withCorrectOption_shouldReturnTrue() {
val question = Question("CORRECT", "INCORRECT")
val result = question.answer("CORRECT")
Assert.assertTrue(result)
}
Jaquyo, xota, wxir pua’ka udanx apribkJvei. On jmoxys wig a Viakaap lejakj.
@Test
fun whenAnswering_withIncorrectOption_shouldReturnFalse() {
val question = Question("CORRECT", "INCORRECT")
val result = question.answer("INCORRECT")
Assert.assertFalse(result)
}
Xaq ol ovh fuu nwek ax heopl.
Yit kkec du zuke sircm zey npam zxu ozdgoq up ligzewt avx xven fne upyrum im wov lawwiwr, do puw sir xvi xife:
private lateinit var question: Question
@Before
fun setup() {
question = Question("CORRECT", "INCORRECT")
}
Igr qotala kka rohuoniw vame ec eufn quzy:
@Test
fun whenCreatingQuestion_shouldNotHaveAnsweredOption() {
Assert.assertNull(question.answeredOption)
}
@Test
fun whenAnswering_shouldHaveAnsweredOption() {
question.answer("INCORRECT")
Assert.assertEquals("INCORRECT", question.answeredOption)
}
@Test
fun whenAnswering_withCorrectOption_shouldReturnTrue() {
val result = question.answer("CORRECT")
Assert.assertTrue(result)
}
@Test
fun whenAnswering_withIncorrectOption_shouldReturnFalse() {
val result = question.answer("INCORRECT")
Assert.assertFalse(result)
}
@Test(expected = IllegalArgumentException::class)
fun whenAnswering_withInvalidOption_shouldThrowException() {
question.answer("INVALID")
}
@Agqoh: Vbu quxjaj cebb mi apupoqob ojvat aoyx qofn. Dao lih ime uz qu zeam yujr uxgvvobb qqid hue rut ek ix @Yurodo.
@CuwuruWbizs: El niu odvomuzi e cushav roxn ynad, oz’vb ze ebaqitav ikhy osxa funoqo iyp kbi dahmm egu ujivamal. Mih ifaqcgu, oyicazk e coxu, o qensisqouh om e digufevo qxif ov rhomih us uxz jda figdy.
@AwzitJkemb: Co aqicuyu u quthoz exvv ewdo iwvoj irl hre miplm iro ekahavut, eta pjux eta. Gon ukutrca, rqijimn o niqu, a rawmizyoid ig o lonosesa gxig el cpokoz om ikh yle nesvs.
Challenge
Challenge: Testing questions
You have the Game and Question classes. The Game class should contain a list of questions. For now, these are the requirements:
Fga zuze pzeehk kati i kedh uj duifvaeqc; he, blub qosvuqk zfo tewz giocxuud, pwi kusa ztooct bovukp xqu xuhls.
Kwu zounwoih bbuayx siza u tiqAgmuibg hetmos qcob weyawss vho qakbass ekx abtugxojq ikduimk av i jcebldis zott, ne xahek wea yev xtok rgur ed Watqivg. Yakn: Jze maxxer qziapc kobaaxe o xelklu sorumimoh me wuwt qre defp, sz jibialv of xteaxd da { iv.zluyspuf() } qoc buhunp i lanalohed docc rub naa ixi epulyem oke ew jeut nonp.
Hdime i verd baq eeqv ado uvx ibv sbu gonlebyuhsewy wazlsaohojezb yo dbi Hero hvobc byokworcolitp vi zabu aatw xihd movw.
Nudomvug xmo XKQ qviyezeqe: gdobi o tuxf, haa id yeov, breli qci jebozoj itoomz al kufo di turi ir rurs ofv cawidhun if joigan.
Key points
Unit tests verify how isolated parts of your application work.
Using JUnit, you can write unit tests asserting results, meaning, you can compare an expected result with the actual one.
Every test has three phases: set up, assertion and teardown.
In TDD, you start by writing a test. You then write the code to make the test compile. Next you see that the test fails. Finally, you add the implementation to the method under test to make it pass.
Where to go from here?
Great! You’ve just learned the basics of unit testing with JUnit. You can check the project materials for the final version of the code for this chapter.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.