DEV Community

Stojan Anastasov
Stojan Anastasov

Posted on • Updated on

My First Unit Test

There are different reasons developers write tests. Some of them are:

  • Validate the system
  • Feedback
  • To prevent regression bugs
  • Code coverage
  • To enable refactoring
  • Documenting the system
  • Orders from manager
  • And many other reasons

There are also reasons developers don't write tests like:

  • Don't know how to write tests
  • Writing tests is too hard
  • Not enough time to write tests
  • The code is too simple for tests.

This is the story about my first unit test written to save time.

The app

I was working on a simple Android app. The app consisted of two screens, a list of items and a details screen for creating/updating/deleting items. There was also a register/login screen that are irrelevant for this story. The app worked with a remote database (on parse.com) using the REST API. You can create an account in the app and you can CRUD some items via the API. There was a requirement for the app to work offline and sync changes with the server when it connected to the Internet.

To support offline work, I created a local database to mirror the remote database. To sync the local with the remote database I used an IntentService. The IntentService was responsible for comparing the items in the two databases and determining what should be updated in the local and/or remote database. This was the most complicated part of the app.

Testing

After I completed the database synchronization code I run the app on the emulator to test if it worked.

Testing scenarios:

  • New item in the local database -> update the remote db
  • New item in the remote database -> update the local db
  • Updated item in the local database -> update remote db
  • Updated item in the remote database -> update local db
  • Updated (different) item in both databases -> update both db

I was doing manual testing. First I would clear the app data (to get an empty database) and clear the remote database (using the Parse website). Then I created an item in the local db, ran the sync service and checked if it appears on the remote db. The I created an item in the remote db, ran the service and checked if it appears in the local db. Then I created two new items, one in the local db, one in the remote, ran the service, checked if it's OK. Now repeat this for updating items, deleting items… It could take up to 10-15 min to test the whole synchronization logic.

Of course my code didn't work the first time, it had a few bugs and I had to update my code. After a few cycles of changing two lines of code then test for 15 min I got tired. It was taking too long for me to see the effects from changing a few lines of code. There must be a better way. This boring, repetitive process was something a computer could do better.

Geeks and repetitive tasks

Testing done right

Fortunately Android Studio version 1.1 (and the corresponding android gradle plugin) added support for unit testing. I extracted the synchronization logic from the IntentService into a Java class responsible for comparing two lists and determining what should I update. Then I wrote some unit tests for my synchronization logic. My only Android dependency in the synchronization code was TextUtils.isEmpty(). As a quick workaround I decided to copy/paste the implementation into my synchronization class. The unit tests run with JVM so no emulator. I run the tests and I got instant feedback, what had taken me 10 to 15 minutes manually was now automated down to just a few seconds. After fixing a few bugs in both the code and the tests (yeah I also wrote bugs in the tests) my app worked as expected.

Android Studio - Unit Tests

Writing unit tests for my synchronization logic was simple because the class had no collaborators, didn't touch the UI and had no Android dependencies[1]. Usually writing unit tests is harder, you need to design your code to be testable and it takes time to see the benefits. This low effort - high reward test made me start testing more.

Do you write tests? Any interested testing stories? You can share them in the comments.

[1]except for TextUtils.isEmpty()

Top comments (15)

Collapse
 
daveblythe profile image
Dave Blythe (he/him)

Simply LOVE the "Geeks vs repetitive tasks" diagram :D

Collapse
 
s_anastasov profile image
Stojan Anastasov

There is also this xkcd comic. The awesome thing here is most of the automation code (the JUnit framework) was already written for me.

Collapse
 
daveblythe profile image
Dave Blythe (he/him)

Yep, the bulk of my unit testing experience was on PHPUnit :)

Collapse
 
andy profile image
Andy Zhao (he/him)

Great post! It feels great to automate something and save a ton of time doing it.

Test writing was a pain for me to begin with though, it really is like writing in another language. At least Ruby tests are like that anyway. What'd you think of the learning part of writing tests? Interested to hear your perspective on writing tests for Android/Java.

Collapse
 
s_anastasov profile image
Stojan Anastasov

When I first started developing, testing on Android sucked. The testing support was ActivityInstrumentationTestCase and ActivityInstrumentationTestCase2 both deprecated now - which means they fucked up twice :D. All test used to run on the emulator. On top of that I didn't have any experience with testing.

As I mentioned in my article since Android Studio 1.1 testing support was improved. We can run unit test on our computers if we don't have Android dependencies.

Of course to write unit test, first you need to write testable code. We have libraries for dependency injection like dagger and dagger2. We have the same tools as in java: junit and Mokito.

For UI tests we have Espresso. Android studio can record our interactions with the app and write tests code for us.

In the last Google I/O we got a few new Architecture components which help us build our app in a more modular and testable way.

As the testing support in the platform grow, my testing skills also get better and I write more tests. I still not at a place where I want to be, but I am getting there.

Collapse
 
ronalterde profile image
Steffen Ronalter

This is great. I have experienced similar things although working in a different field (Embedded software). You struggle with complex database setup and access. In Embedded it's the programming of the software into the target microcontroller and remote debugging that slows you down.

Collapse
 
brainwipe profile image
Rob Lang

I'm all for automation but you need to point out that the automation will cost something forever more. It might still be more efficient to automate but automation isn't without cost; it's not a nirvana.

Depending on your system, the benefit of automation is not always worth the cost. Highly coupled systems (e.g. UI to dB entities) are expensive to test anything beyond the simplest atomic level. Do try to get clearance from whoever is paying for your code because they may prefer the human method as the business value of test automation is low.

Collapse
 
rickab10 profile image
Stephany Henrique A

Very nice! To Start something new is always complicated, but with study and persistence you can reach.

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
s_anastasov profile image
Stojan Anastasov

I have no experience with angular. But if the functions are too big or do too much it would be difficult to test them. To make them more testable you could try splitting them.

Collapse
 
vasilvestre profile image
Valentin Silvestre

Time for some mutation testing .. :)

Collapse
 
saiyamchutani profile image
Saiyam chutani

also check out test Dpc and test Dpc apk

Collapse
 
2014abinash profile image
Abinash Mohanty • Edited

checkout Getapk Market

Collapse
 
dotsp0t profile image
dotSp0T

Obligatory xkcd: xkcd.com/1319/ and xkcd.com/1205/

Collapse
 
s_anastasov profile image
Stojan Anastasov

Check out my comment here