3 unit tests to avoid bad surprises on Android
16 Feb 2016On the road of continuous delivery, an essential stop is unit testing. They should be short, quick and reliable. Sometimes they are our only way to see an error and avoid to deliver a bug in production. This article presents 3 unit tests whose goal is to avoid bad surprises by focusing on key aspects of an Android application: Permissions, shared preferences and SQLite database. Check them out and avoid bad surprises on release day !
First, you must know that these unit tests will be based on Robolectric and Truth (see my last article for more details):
Control your permissions
Managing permissions is often a key in the success of an application. We all know examples about application having a bad buzz because of abuses. On Android, users are very careful about it when installing a new application. Indeed, if they feel like you don’t really need the permissions you are requesting, your conversion rate (visit on PlayStore / applications installed) can drop terribly.
Sometimes, when you add a new library if you don’t pay attention, it can add permissions you don’t need/want (I am looking at you Play Service…) and you will see your mistakes only when uploading your APK on the Play Store. Here is a unit test I wrote to avoid such unpleasant situation:
This test is based on Robolectric for parsing an Android manifest. When Gradle builds an APK, one of the its step is to assemble all the manifests from the libraries you are using and merge them together. Then, this manifest is packaged into the binary. This test will look at the merged Android manifest, extract the permissions and verify that they match the expected permissions. Using an intermediate state of the build is not ideal but this is the only solution I found so far.
Another drawback is that when you want to add a new permission for real, you also have to update the unit test. I agree that this is not ideal however sometimes you have to trade off to be safe. This is especially mandatory when your goal is continuous delivery (see my last article) and you want to be sure your permissions will not change.
Validate your SharedPreferences
Most applications use SharedPreferences
to store data. They are a core part of any application and therefore must be heavily tested. To illustrate this example, I designed a small SharedPreferences
wrapper, I am pretty sure you all have something similar in your app.
Thanks to Robolectric, it is pretty easy to test them:
Obviously, this is a simple example. However sometimes you can have more complex needs like serializing an object in JSON and store it in the SharedPreferences
, or your wrapper can encapsulate more logic features (one SharedPreferences
by user, several objects to store, …). In any case, testing your SharedPreferences
should not be underestimated and neglected.
Master database upgrades
Maintaining your SQLite database can be difficult. Indeed, your database will evolve with your application and making sure these migrations go well is mandatory. If you fail to do so, it will lead to crashes and losing users… Unacceptable !
This unit test is based on the work of an old colleague of mine Thibaut. The idea is to compare the schema of a created from scratch database and an upgraded one. For the new database, we just run the onCreate
method from the SQLiteOpenHelper
. For the upgraded one, we take the first version of our database (like it was in version 1) and we run the onUpgrade
method. By comparing them, we make sure that our upgrade scripts work and give the same result that a brand new database.
Let’s code. First, we need to add a dependency to the SQLite JDBC driver:
As you can see, I also use commons-io to easily manipulate files. Then, our unit test will look like this:
The approach is pretty straight forward. For each database:
- We iterate on the tables
- We build a string representing each table
- We iterate on every column of the table
- We build a string representing the column
These strings represent our database schema. Finally, we compare the two schemas that should be identical.
This is just an example but this schema can be extended since the API offers much more items available. You can see what is possible from the following documentation: Metadata. For instance, you could also compare references or indexes. Once again, apply what suits best to your app.
Database migration is very important and unfortunately it is often a source of bugs. This unit test will help you making sure your migration works and you can therefore upgrade safely.
Conclusion
These unit tests are just examples, however I hope this article showed you that a lot can be achieved with them. To reach continuous delivery, knowing you are safe about database migration, permissions or SharedPreferences is a huge advantage.