March 30, 2020

Quality Pipelines in Mobile: Part I, Android

Quality Pipelines in Mobile: Part I, Android

Common approach

Hi, folks! I am kicking off a series of articles about development and not only pipelines that would ensure the proper quality of the mobile apps being developed. The main idea is to cover major mobile technologies used nowadays: native Android and iOS, React Native, Xamarin and Flutter. I am starting with Android, however I would like to give some general idea about what’s it all about.

Be aware, this is a high level overview of tools and practices you would like to have in your Android development process and not the guide on how to setup those tools.

Why bother?

Let’s start with a well known truth: the later you discover a defect in your app, the more expensive a fix is. Let’s say you got a bug found only in production. Your test engineers should reproduce it locally, report a bug, prioritise it, hand it over to the developers, they need to fix it, create a new build, hand it over to the test team again, who will verify it is tested, then create a release build and only then to push a new version to the end users.

Lots of hours could be saved, should you have several mechanisms in place to prevent the bug from appearing in the first place. I am not saying there is a silver bullet to get rid of all bugs; it’s impossible. What you can actually do is put barriers(also known as quality gates) for your code to maximize the possibility of bugs being discovered as early as possible.

Local machine

As a developer you always do your work on your local machine with an IDE and some command line tools. Let’s see what we got for an Android developer there.

Android Studio

Android Studio is a default option nowadays and, being built on the IntelliJ Platform, has tons of inspections for Java, Kotlin and XML code you may happen to write. The advice is to agree on the particular rules you want to use for your team, configure it on one machine and commit a settings.jar file with this rules to the version control or share through some collaboration tool (like Confluence).

Inspection Settings in Android Studio

AS also has quick fixes you may apply for your code with an ALT+Enter keystroke.

Android Studio Quick Fix Example

Android Lint

Lint is a static analysis tool for Android specifically. It comes with hundreds of out of the box rules you can employ. The tool can be called with a gradle task, provides hints right in the Android Studio and create a report. Lint has tons of checks in different areas: Compliance, Internationalization, Security, Usability, etc. What makes it extremely powerful is the ability to come up with your own custom rules. For example, Roomhas its own set, logging library for Android Timberhas it as well. You can create rules for your specific team or project and ensure nobody is making some typical mistakes. (BTW, there will be a talk about creating one with all the theory and practice explained at upcoming Mobius Conf).

Example report from Android Lint

Other Static Analysis

For sure there are many static analysis tool for Javaand Kotlinthemselves as PMD, FindBugs (it’s not maintainted anymore, use SpotBugs), Checkstyle, Ktlint, Detektand others. Pick your favorite, integrate into your pipeline, enforce for it’s usage (how? keep reading).

Example report from FindBugs

However simply having a tool in place which provides you with data of what you need to fix is not sufficient. You would like to have the following information:

  1. How does the test coverage changes overtime?
  2. How much time would I need to fix all the issues?
  3. What is the code duplication amount?
  4. How can I share my rules across several teams?

And many others. We at EPAM Systems pay a lot of attention to the quality, so we picked SonarQube as a tool to answer those questions. Other benefits of SonarQube you can find here.

Unit Tests

I hope you don’t need to be convinced again that you damn require unit tests for your code for various reasons. It doesn’t matter if you’re following TDD, using Testing Pyramid principle or adhere to a new idea of Testing Mushroom. Not so important what is your target coverage, your unit tests are a part of the feature. So, you need to develop and run them! Great to know that, after 11 years of evolution, we have a pretty convenient mechanism of running tests from the gradle and gradle android plugin. Ensure to call testDebugUnitTest or whatever task happens to run it, and don’t forget to increase the coverage each sprint. As all java unit tests they run in your machines JVM and not Dalvik/ART, so they are reasonably fast.

In Android Unit Tests there is one fundamental problem: Android SDK dependency. This is actually one of the reasons why all these approaches for UI Layer emerged like MVP, MVVM, MVI and other MV*. To give more details, once you have a dependency on the Android Framework in a class, your Unit Tests for these class end up calling stubs, which simply throw an exception. So you either can skip tests for these class, or extract some logic to other classes, or create interfaces for the android-dependent classes to test some higher level logic, or use Robolectric(which is far from ideal). Messy stuff. Other option is too have instrumentation tests, which may be a good fit for, say, Activities.

Speaking of coverage, you want to know which coverage do you have and how it changes during the project, so you would love to have some tools for it. AFAIK, jacocois an industry standard for Java and it has Kotlin support.

Instrumentation Tests

Those tests run on Android Emulator which allow them to have an Android Framework to use. Unfortunately, these tests are slow, flaky (due to some issues with the emulator) so the majority of developers I personally know try to avoid them. But support of them is there in your gradle/Android Studio so they may work for your app.

Security Audit

Aside of plain errors, typos, copy-paste issues and other mistakes the big amount of problems you would want to take an eye on is Security bits. Of course Android Lint already provides some hints on this one, but you better employ some specialized tools for Security in particular. Those tools can work in static and dynamic modes; depending on your requirements in Security perspective you would want one of them or both working on your code. You may want to start with say Mobile Security Framework and later evaluate paid options.

Fortunately, there is a static analysis tools list right by the OWASP here. For example you can pick Find Security Bugsor use OWASP SonarCube Project.

Ensuring all checks

As I already mentioned, the shorter the feedback cycle the less time and money you waste on fixing the bugs. So you may want to be sure you have a production quality code even before it gets pushed to a repo or even committed to the local git. You may of course just ask your developers to do that, but there is match better way: git hooks.

My suggestion is to introduce a pre-commit hook enabling all the checks we discussed above: lint, static code analysis and unit tests. An example of the setup process can be found here.

CI/CD pipeline

It’s really hard to imagine an Android project without a CI/CD pipeline in place. Your goal is to repeat all the checks above at the build level. There are several reasons for it:

  1. pre-commit or other hooks can be easily skipped with ‘--no-verify’ parameter
  2. code can pass all the checks locally but introduce issues after the merge
  3. you will need reports about tests and coverage
Example Test report on bitrise.io

Fortunately enough, you just need to mention the quality checks either in your gradle build script directly, or call the appropriate tasks within your CI/CD pipeline. If you’re having struggles choosing one, I have a decent talk describing mobile DevOps in 2k19 which can be found on my speaker deck.

Please, also do the following:

  1. Run all the checks for the PRs. Do not allow merging any PR which violates any of the rules. This is extremely important as if a rule is not enforced it virtually does not exist.
  2. Run the checks during the build and deploy process. You don’t want to lower your quality bar.
  3. Prioritize the broken build. The team should fix the issues immediately because it blocks your continuous delivery practice and blocks the team from writing quality code.

And good luck making your code better!

If you liked this article, be sure to follow me on twitter for upcoming ones. Also, if you happen to be in Moscow this December or have an opportunity to come, visit our Mobius conferenceon December, 7–8th and know much more about Android (and iOS) development as well!

P.S. Huge thanks to vixentaelfor security tools and approaches; my appreciation for review and comments for Alexey Nikitin; thanks to Aleksandr Bakanov, fellow EPAMer for proof-reading.