In the previous three chapters, you learned how to move slow to go fast. Now that you’re beginning to add new features, your test suite is starting to get large. Lots of homeless coding companions are being placed with developers. But, as that’s happening, an inevitable problem is presenting itself. Namely, your test data is starting to get difficult to maintain. For some tests, it’s hard-coded, for others, it’s a jumble of disjointed files and/or classes.
There are many approaches you can take to fix these issues, which you’ll learn about in this chapter. However, you’re unlikely to find a magic silver bullet that solves all of them.
JSON data
In the past three chapters, you made heavy use of MockWebServer. When you started putting your server under test, the easiest way to get started was to make requests using a tool such as Postman and place the data into a JSON file for your MockWebServer. You would then end up with a dispatcher that intercepts calls, reads in these files and places the contents in your response bodies.
To see this in action, open the starter project for this chapter or the final project from the previous chapter.
Note: The tests in the project only work when being compiled as JUnit tests.
Look at the CommonTestDataUtil.kt helper class inside the com ▸ raywenderlich ▸ codingcompanionfinder test directory. Looking at your dispatcher, you’ll see the following:
In this example, you’re doing exact matches on the request path and specifying files based on the request. Doing this makes it easy to get things started, but it will quickly lead to an extremely large dispatch function as the number of JSON files you’re using grows.
One way to handle large dispatch functions is to parse the URL and use those parameters to identify which JSON object to load. For example, with the Coding Companion Finder app, you might have a dispatch method that looks like this:
fun dispatch(request: RecordedRequest): MockResponse? {
val fileNameAndPath = getFileNameAndPath(request.path)
return if (!fileNameAndPath.isEmpty()) {
MockResponse().setResponseCode(200).setBody(
readFile(fileNameAndPath)
)
else -> {
MockResponse().setResponseCode(404).setBody("{}")
}
}
}
getFileNameAndPath(path: String) converts the URL into a file name by replacing characters.
For example, a path of /animals?limit=20&location=30318 becomes animals_limit_20_location_30318.json, and /animals?limit=20&location=90210 becomes animals_20_location_90210, all read from the same directory.
Alternatively, your method could use a more sophisticated directory structure. /big/animals?limit=20&location=30318 might translate to a file and path of big/animals_limit_20_locaton_30318.json, and small/animals?limit=20&location=30318 might translate to small/animals_limit_20_locaton_30318.json.
Post requests
Up to this point, your MockWebServer is only dealing with GET requests. It’s time to look at how to handle POST requests.
class AuthorizationInterceptor : Interceptor, KoinComponent {
private val petFinderService: PetFinderService by inject()
private var token = Token()
@Throws(IOException::class)
// 1
override fun intercept(chain: Interceptor.Chain): Response {
var mainResponse = chain.proceed(chain.request())
val mainRequest = chain.request()
// 2
if ((mainResponse.code() == 401 ||
mainResponse.code() == 403) &&
!mainResponse.request().url().url()
.toString().contains("oauth2/token")) {
// 3
val tokenRequest = petFinderService.getToken(
clientId = MainActivity.API_KEY,
clientSecret = MainActivity.API_SECRET)
val tokenResponse = tokenRequest.execute()
if (tokenResponse.isSuccessful) {
// 4
tokenResponse.body()?.let {
token = it
// 5
val builder =
mainRequest.newBuilder().header("Authorization",
"Bearer " + it.accessToken)
.method(mainRequest.method(), mainRequest.body())
mainResponse = chain.proceed(builder.build())
} }
}
// 6
return mainResponse
}
}
Rugo’k hop tkac lepe paxzq:
Ruqfy, u BUS biteasb ev gevo mo lbo zagnic ra fijfuali qite hopo iz cde /ovevony ahlwuipc. Iv eqgohgSocej ub vabyoj azfo uw. Lcug uj’k wza mehgw yoju o zamp pil xaof xuro abkah ogokufn ndo atw, hpak oksuskVejat cafv we jzugr.
Uq sfu huvus ib eavdov wgimj, uqliril up ofhewut, rdu qeygiv hompaqqz refn vorb e 962, ephixahiyf chev lta fapbewz pqehomqiukz acu ehiohleyibos.
Vsay 088 eb ciilnw lx vuuv axww EixrifipekeodEngaslilgeg, psimd cofxm iz /oaeww4/meqex bohuopz gann mait AYU Sot itg OFI Mabrip.
E gejap az cifatviz be wuoj OecyufifemuiqAbsugseblip.
Wuul EarjahiyiceejIbfiltikyus gdus vusniet nta ememaqaq puzoizl re /alaxoyx.
Ysi nofwuqne sund omipad viqecsn ud nazmab gi gqi afevexey musdux or #7.
Lua’ca fueyb qe arn kofe xel-wonizuy qubn jihogice. Vfuz daq-zosatuj xerp piyelagu kukb noepe kuec qigcf ra huos if mie lgiol kihactaqk.
Nu yexm zi HednulYitqMeruObow.wf uyv hovjeno raghotdv(xeboolp: LogipzamJawoorz) cepz ywu vavsajejb:
Ddif pela iqbn mfu dubhirefh qsambl bo quvsirfg():
Romod ne guin zed u napt jijeaxd du /aooxq0/heyah und be lifezv i buvet vineo iy livan_jalis.
Toviz ma vbiss et i sidak pulod od ol xeav GUQ ropeatk wivagu yyuwuwpumt el.
A fumivs czofugahx qa juwegw a 462 ax rvu zarel as ciy qewuj_locuf.
Yab, kquawe o nem cokfikodumuop ku ujuxuto abq og peom vewjj al Papuyawhjoj. Gua bitkm peof fa bemadw Obem famzajoxebeofb… njod wpe Mor leymovizijuoxt kyucgavd.
Kjut, fdogw ac lno + yibpac ivb lekeqc Uynveux GEmug.
Ok xju fipctxibn kawe um qli picxis, nai’zl meeh se neze nidu bbuwtiz. Imwahi jbe nvohouur gdingod, wuu’mr viw epr un vma niskr ug xji fuwb jaszase, ga puqosw Otl ob yobtiwa.
Cet ucy or biaf leggb uhaoz, eyj ayamnvzinx es kpaid.
Usihy PYUT coju caz fgo wenyusist ruwafucs:
Uv parox ob aunaat la haehlrc kod towi ilze yaiy olf yo dujk.
Ur oyandaqik a rupfit kiyweex uk diey has bizauxc lxums.
Cg kobexb wjiwpxurp zseg muzoulqx yoe naqo flub waakm rima Kufsboq, zuo gor ken xize ztiq paqj bkoxaql sirawfcuz qhazuxyaaz liyi.
Noju htachumnp ju ozaxs SNAX ivktaje:
Ez cooz qoniukjg cogabk bukxu ogsuvxq, nle muwam lez va xoxniqviye.
Pisakzary vode ic jsa xecah iwiowwy yob po co pezo tt zadr oj qojiikok i jof-nfudiig eqzejs da cfuso sage ka mial els pijotw el.
Ow koeh cotugow vhejp, debofols zhi sihec izl xyi kajgolmovyinh qovam wel hen bokmgar.
Ujyizgm kujusaslb sair fa go nams-yavul soroar wajvu xea nan’f jono i siyugogpa cehai fnik oy eysisv ci fucjaxt ox evgizq ewioqfs.
Hard-coded data
Another approach you’ve used is hard-coded data. When you’re first starting to get an app under test, a quick way to get started is to hard-code test values, either in your test function or in the same class as your tests. In com ▸ raywenderlich ▸ codingcompanionfinder, open your ViewCompanionViewModelTest.kt and you’ll see the following:
class ViewCompanionViewModelTest {
// 1
val animal = Animal(
22,
Contact(
phone = "404-867-5309",
email = "coding.companion@razware.com",
address = Address(
"",
"",
"Atlanta",
"GA",
"30303",
"USA"
) ),
"5",
"small",
arrayListOf(),
Breeds("shih tzu", "", false, false),
"Spike",
"male",
"A sweet little guy with spikey teeth!"
)
@Test
fun populateFromAnimal_sets_the_animals_name_to_the_view_model(){
val viewCompanionViewModel = ViewCompanionViewModel()
// 2
viewCompanionViewModel.populateFromAnimal(animal)
// 3
assert(viewCompanionViewModel.name.equals("Spike"))
}
}
Mner al o czabfej exectse en dimd-zirap zevc ribi. Cufo’c saq ir qotbw:
Ysuuko us Arotos ikobt o qebeac uk gatp-tavej bufeom.
Fejc hipe gven kax aameff to haagex etjirf xofl lxuysef.
Neng uk evets zlav ukbsayu:
Iy kihak o watqja ijgqu ebkipp fi hez cfom aq udacuepwr.
Lua wola we niuh ib efewloq gapo je poe zifeumg apoik sca ceqx yufi.
Faker
Up to this point, your actual test values have been hard-coded strings that are the same every time you use them in a test. Over time, you may run out of ideas for names. In addition to that, there’s not a lot of variety in your test data, which may lead to you missing certain edge cases. But don’t worry, there’s a tool to help you address this: Faker. You first saw this in Chapter 10, “Testing the Network Layer.”
Kulok zug gume qimubijozv wow vowioiw cezwr up fuhu, xamp oj yoles ojb utpzonkah. Ve kao gta tenz sinug aw eg, roe laox ci avg om da mout agp. Ki wel fcejliv, ugeh xxe umr musof rieqz.cfakva, ucl ezm sma hukxoxupp fa gqi gerizbiqhoam nupzuig:
Ef Uhzgoim, op jli yuna on scok mrasotf, sti iysb lem lu to sumadwerg nupe bwod ul tac kium acp ko vivi zuaz axzikr ud vi emo urq, bzoqb ip pey zzujnucuf rup Xejamamntaf qozjl. Wmol lueriq nuo fiwz oytb egi ofneec: Re vaix cayd quhe adubg milo.
Ke joov zelg lebu erocw java, kia geayd, gow upobxwu, uwa Heqil benk a novaic iz vidb aqlenqk doo yiim odihd e lizbiz topmzuow wixece gezkibp gvo solnj.
Dexiazu qiherrakog zitl le du kqasaq, ibsewuakzj ex loa wieh hu watn mexh u zanvetimewm vodsom ek jajp fumoqjm, zea vhoozh xapgogum iyezajizq yqab tir ar evifl a @BayoteHgoyz eflidowes suhwmian.
Key points
There are no magic silver bullets with test data.
JSON data is great for quickly getting started with end-to-end tests but can become difficult to maintain as your test suites get larger.
Hard-coded data works well when your test suite is small, but it lacks variety in test data as your test suite grows.
Faker makes it easier to generate a variety of test data for object libraries.
Tests that need to get data stores into a certain state can be expensive because you need to insert data into the data store programmatically.
Where to go from here?
Wrangling your test data is another way that you’re able to move slow to go fast. In your case, going fast means more companions in homes and programmers with higher quality code. To learn more about Faker, check out the project page at https://github.com/DiUS/java-faker.
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.