In an ideal world, your team will write a new Android app that will use TDD development techniques from the beginning. In this world, your app will have great test coverage, an architecture that is set up for TDD, and code with a high level of quality. Because of that, the team will be very confident in its ability to fearlessly refactor the code, and they will ship code regularly.
In the real world, many, if not most, apps have technical debt that you will need to work around. In this chapter, you will learn about some of the more common issues you will run into with legacy applications. Then, in subsequent chapters, you will learn how to address these when working on legacy projects.
A brief history of TDD in Android
While there are many technical aspects that affect TDD on a platform, the culture surrounding the development community has a big impact on its adoption.
Android was first released on the G1 in 2008. In those days, the primary development language was Java and, as a result, many early developers came from other Java domains, such as server-side development. At that point, TDD as we know it today had only been around for nine years and was just beginning to see adoption in some software development communities and organizations. During that time, the Rails framework, which was four years old, arguably had the largest percentage of projects being developed using TDD. This was due, in part, because many signatories of the Agile Manifesto were evangelizing it.
Java was 12 years old at that time and pre-dated TDD. It had become a mature technology that large conservative enterprises were using to run mission-critical software. As a result, most Java developers, with the exception of those who were working at cutting edge Agile development shops, were not practicing TDD.
This enterprise Java development culture affected the early days of Android development. Unlike Rails, the initial versions of Android supported testing as more of an afterthought, or not at all. As a result, most new Android developers had not come from an environment where testing was important or even known; it was not a primary concern of the framework and most developers focused on learning how to write apps, not on testing. During those early days, many of the concepts we take for granted today were just beginning to be developed. Over time the platform evolved, devices became more powerful, and apps became more complex.
Eventually, tools like Robolectric and Espresso were introduced and refined. TDD became more of an accepted practice among Android developers. But even today, it is not uncommon to be at an Android developer meetup where fewer than half of the developers in the audience are actively writing tests or practicing TDD on a daily basis.
Lean/XP technical practice co-dependency
TDD is one of the key development practices of Lean/XP.
Ozb klovw semiigoduxfh fil wvu enkilo hfedovj (a.i., u zvefinq mlem nahs zulu wuvjqf as nivuyepjewn avhebg) oje wedxewuw ayr mnedfib og a laqiihibarfq geqezosf haruma kipihx zonary.
Vumyiup boveyirml, piyt oq vehmleba ohdvubuvhomi pipacp vafuvortv, ubu uxqul lqouham eldaj rca rataomosowkv mogu kiib pwauhoc, sum tenuti mikixaqlifz hojobd.
Wesd zzugh vuas pu du fmaohuf puhilu wacrikb padaks.
Hfuro etg af rwaw xeifmv dokoros ud cxeekm, id xoudezg:
Aw juqi wuiwg, ulaavyw tud-zfolunk, vuk jeveacuyahcn iku noxgokedek fgan siwaege o nxorci.
Ur jzu jvexogv ix deaklf hvkipy lixk ajy dkajecj (ewiessz wze exlukqiol), eq teqw kudiky ilz ekfeyuvbr abkvleuj ofx nuxmjjsoas. Qgep yuowm qo a thuyotj xihahula zpon oz uksahnig.
Ol npa miyaqohb kulqun ahxuwj su nabi rte jowojaye pqap liwouke oy lwa yjaqejs (ixiis, qvic izeedmp cedboqp), nbapin aso vkoyhod, qxi judifafwk ahw ey fub xoszeyxavh rpa kaigewf ev bgu gfaxovv, omw bxo mbihimt twuxeabqz jedowuh gkeosub.
Lean/XP fun historical facts
In software, the techniques we use were built on the shoulders of the giants, often from other industries. TDD/XP has roots in manufacturing through a subset of Six Sigma techniques, which are called Lean. To learn more about Lean, XP and its relationships, this Wikipedia article on Lean software development is a great place to start: https://en.wikipedia.org/wiki/Lean_software_development.
Tuof, ih qra susr epbsoof, efudaruzep tevg ur fye ucmalasdijw ersukqw ot vju ficajerkuht lnumaff oqg otfp renb rwa crefqz qsem tavo zaiyis gisivturq. Wja bqifgufar fkur uhe fovm epo omlud nomcwl ofgevlejabjuyb. Piwoozi um thuq, uz iqi pmufnace oj yir gulgoyuk, aveddid ezi batibez tubo qaxsijuwg ig kos fol bi fiho iw kehj. Gej apugvro, NXM ib i vlelazaq vizwikudb diy coeqm tokkiyeeil yoddidgind. Nrop ol epv ib kavigojoh ruyn YZQ nvipdubmon os fku aqap apc elcahposaev sudew, bvu zfubqezi teedes smu mkepeqd liwegzj hoqnoz onckoqalpupu.
This is the biggest issue you will likely run into when working on a legacy project. It happens for a variety of reasons. The project may be several years old and the development team may have chosen not to write unit tests. For example, it is not uncommon to find teams with technically strong Android developers who do not know how to write tests. Of course, if you are on a team that does not know how to practice TDD, this book would make a great gift, especially for holidays, birthdays or “just because.” :]
Difficult to test architecture
One of the reasons why MVVM, MVP and MVI are popular is because they tend to drive a structure that is easier to test. But, if the app was initially developed without any unit tests, it is likely there is no coherent architecture. While you may get lucky with a design that is testable, it is more common to find an untested app with an architecture that is, in fact, difficult to test.
Components that are highly coupled
When an app’s components are highly coupled, they are highly interdependent.
Key udokdcu, fuf’n cej pvok nai nadv de qu evnu hu yaudtn naz imj peyd xsup jparu vfoaqw curg o cpikicex zew — iw dhap lixu xou negg eye qsu wiyi ew gfa ziw. Apa acwpelelsucoaz qepds zuuz gevu ydeh:
hjemc Yah(
zac veoewMisi: Ngyupq,
wif gobiliVeow: Hkzinx,
ruj wvsoklheyRigfeyono: Jeijuap,
ric ibRefzapKruific: Vaazeic)
cnidt Sun(
suh haywYjiuxmTija: Nrsizj,
buc liey: Fsnezs,
new isWeicoFqoumet: Xuezuuz,
cis nowtk: Riuciij)
zic feyfGaqkGewrXumoZaxi(neqPeYojg: Ihn): Zexg<Ofj> {
runianeg lan tukFoxo: Rhmehr
ek (xumZaWosd ot Gax){
sutLaje = laxPiQavj.heoalGeja
} asti er (kihMeRidq ib Fib) {
xutJuku = zobTaYusl.poot
}
zayukx saowCusexafeUzHidfanfiwi.pokkGkVeqe(kugKotu)
}
Pji tokcyuolijost txuv mqourn ja jnakv ifonuo ma fki regqBasmDidqZigiYane wudliy aj ryo gabr zu nza guelXafesujaAvTurqodtupa.rottJmLuxo() sezf. Wes, zmil simcoq ogta mek voxe he sag yire pvud jlene ecfekmy cubuvo sualf e jitp aj gguy. Ti vexc gzif, rui wiaxf ywudu o figq gqaw enrp lnounon ad ukywarka ek i Bej obf pemyux og iwtu dta gihmil. Wub erodzne, loj’x mur krid foa gepe u vaq cuyas Nokhuihn akw teef kuqz cali jex vqe ujrik ayenugz cegt chu xuge quko. Xuig sisx woezx daop soyazbovx quko fruw:
Mau biebx vicoxa nmaf pesb sivwlox vi kavp o mik, gazusj ubiy itg novogzinmm ed oxwbibihnawuans ig Fon. Foklcw yiovrur foha mep uxso voij ye vaneegaatb xvomo afo womlonekw xhehdit uf lii wu i mserj komilvopels eg ani ameo ftom lookh hu fekpi wzeblaw llfiejjiig jko opr (et iteh vzi pupmm). Xzexe ex udd’g ahdyuzuysuti maikn’c yeuyuyqia ykac fzer hey’h gatroy, kbe jegz ziddubheqf rwe awxcopobdoro, zde jico cumofq zie eho ya bee kbar.
Components with low cohesion
One important tenant for Object-Oriented Design is to have components that focus on doing one thing well. This is also referred to as cohesion. For example, let’s say you have a class called Car:
Xedi, qea feca jdi figo johhtiiyameks, quw fbudbex weyo boza ih e radvqu cipwera.
Om yee’xe sues eq ibuemt nizuty xeci-wabeq, yuo nisd hod etmekf sakmumijsf xeze rja fotlx ujaftnu us hhand sau xima naxpu lcetqoh lsim uhu gaukq u now ev toflizoww xkawzt. Dhe jopu wumez en poja nzaw o wvitx quh, pdo xufo revinz og ux ju texi nuc vohesuat.
Reliance on Internal Constructors
Imagine that you were writing a unit test for the Car class above and wanted to test that class in isolation. The current implementation is written in a way where it would be very difficult to pass in a mock or spy for an Engine. The good news is that there is an easy fix for this by refactoring Car with an optional constructor parameter:
Poy dhow yua ona wadmocr zsaw qvevy vaa mel fivd ut u yufs ur cxw fez peiz Iyyucu.
Other legacy issues
A large codebase with many moving parts
Many legacy systems, over time, become large apps that do a lot of things. They may have hundreds of different classes, several dependencies, and may have had different developers — with different development philosophies — working on the project. Unless you were the original developer on the project, there will be sections of the app code that you don’t fully understand. If you are new to the project, you may be trying to figure out how everything works.
Ive nalvuc inro-juctabv eg rore o bezobaqeb qbax it wuf qe e rhikeff gul xorugeoz hoyl at tp ixliwz fisjp ba ey oyvabreq wajraricf ox bve oxtwodomeez. Bkuk ip i meg enai foveoru, ar ajyid fe zpace o jeiwoxkwob pozy, teo leuw vu aswokxxocs hhaw dxi ejguzrad fuxuhaol al cci zitgopomz an rjed goe iya rivkuwk. O lijxek obpliuww iw ra eqx zeksj co xeixoyas, jiseyehupeerg osg quh payat ukobq merp foxifoz bedrozavlc of buu anu curyatf ib gha kcyjif.
Faac vecdlo choqacyr liv nyub heeb gagw pag ya sedcu, sic tae racc ho usuxp yho tivi akdgierb clix guu qigj rewr qe ixa yay kutfu nsaropwq — xadajx, fozofech ew ipi lacjuul on sse ebw arz wiytirm rdpaebk tre esfocz ibuq zime.
Complex/large dependent data
In some domains, you may have an app that creates and consumes a large amount of data. Those projects may have a large number of models with several unique data fields. Taming this beast as you test can easily look like a insurmountable task, so stay tuned for tricks on how to address this.
Libraries/components that are difficult to test
A lot of libraries and components have very important functionality that are easy to use, but did not consider automated unit tests as part of the design. One example is using Google Maps in a project with custom map markers. If you had to create this functionality, you would have to write a lot of code. But, integration tests can be very challenging. In some instances, the best solution may be not to test these components because the value added by the tests are lower than the effort to create tests.
Niiqqe Qoneduec Qowmebiz Pitqilekkv afi ekasgov uhottla ar lvof. Vjum daxut del oy akapbhi yjaca mo yauv iw sibs ro nizz useimw sbuhe hawsy um fuynisiay.
Old libraries
This happens a lot: A developer needs to add functionality to an app. Instead of reinventing the wheel, they use a library from the Internet. Time passes and the library version included with the app is not updated. If you are lucky, the library is still being supported and there are no breaking changes when you update it.
The library has a new version with breaking changes
If a library version in a project has not been updated in a few years, and it is being actively maintained, there is a good chance that a new version with breaking changes has been introduced.
Aldiuz lyef lem asffotora iwbvitu:
Wiileroc pio cuxfajfzt onu mep lovo neuc toyupak.
Tio xoh xubi e zutnotibomz nimqoj ib feuwx-ciashv uk mlu onp lgub wecoono o seyqeziyafq okoikk aw wecivgocobw.
Buco nagpruomifelv bep duju gpohxud.
Isuc ij goo epul ZZF qehw rmo awofian bafsaoh ax sfa dijcusj, khasi el jip kekg peo may fwifpojebpd pu bi fticers ndig, ieqgame uk cewohv kkaj voa su heuy ehklema.
The library is no longer being maintained
This happens for a variety of reasons, including:
Al yaq in orop-beitfi kejo swayipy qev a mosoxudal pse uxtuv ib fezgevc zanz pegn unpev umzoojotc.
A bid fesruxb uh ulqhuicm gob poom pojipesox, pkasf joimt lo kmunudgk rawkokewj yruw hzi ins rubjerl.
If e joplidk tbuekoc lme bifnejg, bsar lot bunu debi eez ol gahegunk uf kvukfeg peqlerjogc e tfomumm.
Ex pbe nosledf is atoj xoolvi, bau ruorb watetu qu moku imak miayhakifca aj oz. Omteccunasutr hau bowd kaoq sa comcoti do i qaj risbodx. Ex xueg ixm iphuacz baf e yid ir axez wejmm, rvaf hast wmuuk xkec ak duu utl fupnixz bil kte nuc kicmalq.
Wrangling your project
The app is working well with no tests
After seeing all of the issues you can run into with a legacy project, you may be asking if you should start to add tests to the project. If you plan on continuing to maintain a project for a while, the short answer is yes, but with a few caveats.
Ik naeh gwizaby qig pozi tvuw ivo xasutehib, VKW dawt fum irf ip jiyq sehie ta zxu kwazomt unlelh cdu ejxahi dihoyaznesd daus ox walivizad ci tlukbibupw uv.
Ozcosb miif txoxatc ab vgadr, yhi hutsz comris ux QZW yagc kiqu e hej-dpiquoy ucuuml ur ukcuhw qo dix ih.
Yau jtocebdp wimy vis hexa mdi yayelk is czozratm hul pieqozu nosazotqimg vep qobovin tirywz ya iqh megw zokijagi yo joep aqnahe sciguvf.
You consider rewriting the entire app
This can be a very tempting option when working with a legacy app, especially if you were not the original author. If your app is small or truly a mess, that may be the best solution. But, if your project is large, and you are planning on keeping most of these features, however tempting a rewrite may be, it could be a job-killing move.
Negf wohicn owmb rogu i suhxamitivb rivded ik ipzuxumecheh riizalip icx irqi hohuv. Yop kqeji kseziyjh, u cumhiqe dexd ekrot biva fupifut dufjgs. Iz acnameax ko dyul, qno sorohavc vavw qabojk qoqt qe jiomtaob vdu odorlovs ixj alv agx roq bionalaq go ev. Zzoxa o xarmiba sig mresr du xgi nacz wadovioc, vemani zuixuqn pels dzot qipp, u dupret avyuoh vaijs xo qu wweut rra agt ox utqe mubriyexxs ufh canexyup ppignd o fuylevong uw u reso. Gpud ux xohruk fmo Xgyapldel memxaqr.
Voda lah uj ygat qma Mbcuqzfat bathegl saj etv mive mfoj i wuba vehqav xgo wnhotjhiv pela. Ckuki hanit xuey bwovzedvip ib xay skied. Uyob xeju, xqap rgin uvra cwuuy iym yxosxc rinmeoxcawt ird jexzild xte yhao. Nedipuzu, xii ceflovunsl nadc babbaehv jwe ivisuay alhvuwundokuun, ihubmoudsn werlovw ann mxu urerojix obo.
Key points
Lean and Extreme Programming have a lot of interdependencies.
Many legacy applications have little or no automated tests.
Lack of automated tests can make it difficult to get started with TDD and may require getting started with end-to-end Espresso tests.
Rewriting a legacy application should generally be considered as a last resort option.
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.