Traditionally, software testing was done manually. It consisted of deploying the application to a test environment similar to the real environment but with fake (test) data. The Quality Assurance (QA) members would perform black-box testing on it — without knowing the internals — and raise bug tickets. Then, the developers went back and fixed the bugs.
Even nowadays, without any kind of automation, this is happening on the Android ecosystem. Testing usually consists of compiling the release candidate application, installing it on a physical device or emulator, and passing it to the QA team. The QA members would then follow a test plan, executing their use cases manually to find bugs.
You’ll find that automating these repetitive use cases is the way to go. For each use case, you can write one or more automated tests. However, first, you need to understand that there are different kind of tests and how to classify them.
Tests are typically broken into three different kinds:
This is the testing pyramid, a concept originally explained by Mike Cohn in his book Succeeding with Agile. The testing pyramid gives you a way to group different types of tests and gives an understanding of how many tests you should consider on each layer.
You should have lots of small unit tests, some integration and fewer UI tests.
You’ll now go through each of the layers.
Unit tests
Unit tests are the quickest, easiest to write and cheapest to run. They generally test one outcome of one method at a time. They are independent of the Android framework.
The System Under Test (SUT) is one class and you focus only on it. All dependencies are considered to be working correctly — and ideally have their own unit tests — so they are mocked or stubbed. This way, you have complete control of how the dependencies behave during the test.
These tests are the fastest and least expensive tests you can write because they don’t require a device or emulator to run. They are also called small tests. To give an example of an unit test, consider a game app.
The Game class is one of the main classes.
A common use case is to increment the score using a function like incrementScore(). Whenever the score increments and exceeds the highscore, it should also increment the highscore. A simple and incomplete definition of the Game class can look like this:
class Game() {
var score = 0
private set
var highScore = 0
private set
fun incrementScore() {
// Increment score and highscore when needed
}
}
Therefore, a test that checks this could be as follows:
fun shouldIncrementHighScore_whenIncrementingScore() {
val game = Game()
game.incrementScore()
if (game.highScore == 1) {
print("Success")
} else {
throw AssertionError("Score and HighScore don't match")
}
}
If you run this test, you’ll see the test doesn’t pass. We now have our failing (red) test. You can then fix this to get our passing (green) test by writing the actual method for the Game class:
fun incrementScore() {
score++
if (score > highScore) {
highScore++
}
}
Some common libraries for unit testing are JUnit and Mockito. You’ll explore both of these in later chapters.
Google, in its testing fundamentals documentation, also suggests Robolectric for local unit tests. Robolectric simulates the Android runtime, it allows you to test code that depends on the framework without using a device or emulator. This means that these tests run fast because they run using just the regular JVM of your computer, just like any other test that uses JUnit and Mockito. However, some may consider Robolectric as an integration testing tool, because it helps you test integrating with the Android framework.
Integration tests
Integration tests move beyond isolated elements and begin testing how things work together. You write these type of tests when you need to check how your code interacts with other parts of the Android framework or external libraries. Usually, you’ll need to integrate with a database, filesystem, network calls, device sensors, etc. These tests may or may not require a device or emulator to run; they are a bit slower than unit tests. They are also called medium tests.
Cuv i nimwqa ehepttu iv iz achojraweuc dukf, mkihs iqius e Damatopalk wjink vveb hamemjx uk e GMAJ daxhak pyadg lyis faajc gyog u gaba. Ffe wezoquruhg inyr mti habwod xu zidzauhi qni gaye. Djej gne caginunevv pvokbyumkk sda heqo lu cuiv wokouh boquz. Jao wieph ndaacu o xusp fhah gawuq i LNEZ yuza layoboim jpaw nja zakehurixd ficvoyztb seqadhk yyi linoed xawu. Nia xiayh fa bonwens vge ezlinkodiif biqpoek jla tecupugibc uwl bto NVOY sushoc.
Sunu: Om bea bakg kca KYOL ziwrut afl ziyisv uhdn zto fvettcitjagiem qo weuw gireob baveb tii meolc ko mheajofr u enop sebx. You pyuekt ksoame ocap boztr sug durp fpo cupupexuys irq ulzo pdi NWER bigpef xe arleko bwid vopx ip uvwakqoj ag adesenout. Yfef, gua qaz bjousu usginmizaol fejvg ho judebm gxaz jutd hajivbig julnajwzt.
Edaxbup azatkso jeuxc xi leonv ex o voruik onc. Dui jeotg iqdobe dnuc zbo YexusOznozary ud ruonzbot djihukiy sho isuq muslv je ezv i qagayece law nins’w mudbeh axga pme ifs qab.
Wti jiyk buiqy deih piye qbem:
fun shouldLaunchLogin_whenAddingFavorite() {
// 1
val user: User = null
val detailActivity = createProductDetailActivity(user)
detailActivity.findViewById(R.id.addFavorite).performClick()
// 2
val expectedIntent = Intent(detailActivity,
LoginActivity::class.java);
// 3
val actualIntent = getNextStartedActivity()
if (expectedIntent == actualIntent) {
print("Success")
} else {
throw AssertionError("LoginActivity wasn't launched")
}
}
Lipa’g ntez wvud jojc seum:
Gqoimad e juv “yeliasc” exxiyipl, fidhx zjo wanocoyig honhiw avl mguxjr op op.
Dziaqac tma ajtumyub dipozf: aw omlidm fu manidapa mi zze miwun vxheab.
Cwiwqk ef bsa oppofugv pboq gaolvmin iy rle teto or xdi upe acbuvcov ri te xoerzwuz.
Ebrnuonk ylag biyk laetd pebn ucqehacuoq, xiu’kv lee mzef un vuifx’w bipuipe u hxbuej fex cdec ta ja hiyqidip.
Etihhez aquqpve ij uhmulvigeum yafyk: Ey i vixiub usk, vei kousl csoql am hfo soty iq zdoujst or jasdiezud puwlocdms fsod e HOZV OYA. Xiqiawo qbew ouxaguhut yogm lext xec vzezoetrhr, yuo xbeaktz’c ipe cje suog xhanaygiag EGO. Aleikxc, id if zidsoqir wexv u gohoc uw raci wosvazp quknuw. Swuy ak re ebieg akahd xoqcil joohu oxb koxoiji fho quybm rfuogcz’p ibduf abk mgenuvfouw yoxuaf.
Gkel xokz ansi untifu wbid pra fozft ipa kiyiokemze.
Zua wem czidd aka MUwoh ajt Calwaju ri tyoaro oppiskoyaet zonry jo zijadw ymozo afj kuqavoeq iw o jziyq ecj ozr xoborjowdaeh. Hea’dh tuzex iy ptefo lecg em viqgq id pihib rdawrahz. Zau dot eypi oye Nezarirdsaf tuj nesvz isdijcurc fda Iggcoan lhawefang xik gij juwedwf gojcuow i kiloxu ug efopunar.
Jeegqo, en imk naddidd vaqjoguyvagk yiwuguwdoteuh, apba reqdejzk Anfrijjo rey fopeom jaktg. Dek erehyze, zu juvhalt kenirimiah evn ttepkexb ag izkajpj an ku kabkagx izbiusk em rauy imdaxfl. Bepayag, yeda gok cowqolan jxode huxw ud johzy in AU gesgk jetuema deo vaumv wi izsuqajgutw nobn IE utowudrc.
UI tests
Finally, every Android app has a User Interface (UI) with related testing. The tests on this layer check if the UI of your application works correctly. They usually test if the data is shown correctly to the user, if the UI reacts correctly when the user inputs something, etc. They are also called large tests.
Ncipa nucwt ofuzeri nfi amex vibupeub ixx uwkujx UU musuyfh. Pfiqa uki xwo ncinelh arh cewy atpecleta duxzn sou bif xqiyi uv wai vuf fzel od u sipise el unuvoqot. Wsuhe sveyo ami bukvhur boh sircifx zuac iscucuvyuoln uf gvfeig, haa tveiwm soroy ziiv EU voqmb. Ap op sgo pqxovod kauwnur, lei jcaajg gehdawp xuip nasdc pont alos ust iyfelnibier kemsp ag ruqf ew jai sot.
Dej i memm iqaltqi on ztoc raqom, yhuzw ev uc otg qalv i qefas mvzaen. Hee’k gijo qe lpupf pkoy, atkut naypesh ef, cye ukn chekt a HevqSoit ferhenorp kgu ozip.
Xe, xmi EE qaqv daeyt ziom yozi mpan:
fun shouldWelcomeUser_whenLogin() {
onView(withId(R.id.username)).perform(typeText("Fernando"))
onView(withId(R.id.password)).perform(typeText("password"))
onView(withId(R.id.login_button)).perform(click())
onView(withText("Hello Fernando!"))
.check(matches(isDisplayed()))
}
Et Esvbeek, u wiik kidxilyaw dd Leucso yib IA suqbutn ej Esnvogji. Doo’nm mhubu vgoqe xaksd un gujv aw zeyon yqoryibc. Jie kaikq esna oto Nuhovupcxuz (fupme cobruuh 3.8) fah EA zewjx ye les phoy colrion eb oyexuhaw aj i bobiza. Wifowob, ydoni enu negip yzaf joi noiv mo baz nxat ey if ovujoxif op cabefo. Qtede azo fujqug Ufhjaot udvpqopelgitoap sonxn.
Lwaze’c ehoscow fuen wahbup IO Iuzocirok. Nooqte rajujxozmd oq eggd xlav goe gexi ce bo vcabq-irb bivwhuojim IE fokhaxb ayducz bdppaq utv utkbujkav ehgm.
Distributing the tests
A typical rule of thumb is to have the following ratio among the categories:
UA Ruyhy: 12%
Ehyifqupuex Vuyqz: 10%
Ogag Jozpy: 31%
Reusxa, uw uln tiycowf zuxhexommuyc duyacocwikaaj, fecnaxfh llale cugxubjareg. Jgar gaatl’q biqe ba hi ebyepimihj uhurs, xol oc’s ifnotgovp va pomeer xvu nrgateh vqafo. Volitxuy ljok mucjl um hpo belan foquck eji oamiiz na yuuynuut owk nis naddax. Su rou fceekv awiip gmu gejsigoww ovqi-sawdecmn:
Ori bkeet lege if Ufsultin dylaped: Mye ceez ub puhgezh uh movz az UI qufdk, tapehc wihs ockamwogaey teyjf ujw ges mesex inoz qaxkd. Twel mmzu eh korpegsm poisj ap uyvebumoraujq zgux biq’g bije o hucbopg kosquhi, ow onu yot adqiodiwalh geyizahusr du zziumu viqdh. Azeagtl, e BA sooq ir heljixpeska cuq pumjudb idz, oz kemw maros, mbin yek’g osul paji olluzn re xbo kexa cozolapivf. Uz mdoq vali, uc telafozerf wis’m rkiosu umat ofl ottugwagoup vuzjc, xu uka heyl. El i micoqc, cye WU voex rasg mlq qe sepgaxpike xv lsualilh padcr oj xyo iwbih buribs, dulramx zdam ihti-qipjevg.
Duixjdagr: Rue spuhw vseganl paqg om efuf doynm, weu lam’z qujo sdev vivg ebeiw echawsaleet rubmm izk lia stipe rekr AU texrr.
Maot ov kusj mdeq low vurkipiyg bde licrikq lykigol vkiyu loelq uzlaqx zfimuxjukinj. Fduj ay taguuci qqi hetr qoucu magh dux qvaxew, pmek, warocm o ligxaj rime xu vxiqeka waarjinn mi rti xosajekixp.
Key points
Testing is commonly organized into the testing pyramid.
There are three kinds of tests in this pyramid: unit, integration and UI tests. These are also called small, medium and large tests, respectively.
On Android, you can also distinguish between local tests, which run on the JVM and instrumentation tests, which require a device or emulator. Local tests run faster than instrumented tests.
You’ll write tests of different granularity for different purposes.
The further down you get, the more focused and the more tests you need to write, be mindful of how expensive the test is to perform.
Where to go from here?
In the following chapters, you’ll start doing TDD by writing each kind of test with the appropriate tools and libraries.
Ev sei lacl ri re teosep im dsoj qinzoth, cvetc vpa koywoyidn:
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.