Locale bugs & currency formatting in Android Studio

I grew up in the United States and like most countries I’ve traveled to, I’m used to seeing money that look like this:   $56.33   €12,50   £281.71

A modern IDE tells you when you’re ignorant

Today, Android Studio was kind enough to tell me that some sample code had a problem: “Implicitly using the default locale is a common source of bugs”. Nice.

Looking at this sample code, I asked myself: “Why should the UI format this number, isn’t that going to hide what the underlying application logic is saying?”

So it was a great thing that Android Studio shows little messages like this because it got me thinking about how to properly handle currency in the view layer.

Simply changing your app code won’t save you!

This is a case where view layer formatting isn’t the appropriate place to deal with rounding. The business logic of this application, the algorithm that calculates tips, should ultimately be in charge of rounding to the locale-appropriate currency decimal place, in Java this means using the BigDecimal class for manipulating values. So, I forked my own copy and updated the sample to remove the view formatting code.

Then I changed the locale on my device, and my Espresso tests started breaking.

See, when you write your format codes and test logic under a narrow mindset of one locale/language/currency (en_US), the test data you use can break your tests if the app is run under a different locale (ar-IQ) which format things like currencies (USD, IQD) to a different arithmetic precision (the dollar uses 2 decimal places, the Dinar uses 3). [locale/currency lookup table]

An example of an Espresso test written assuming western currencies can be found below. Dinar has three decimal places, so this particular sample fails because values are handled as doubles (floating point numbers). Without controller logic to deal with rounding, it comes out as 35.912 which is not the same as the 2-decimal data “35.91” in my test code.

To simplify things, much of the code is written using doubles to pass currency around, even though this isn’t best practice. Even still, so long as we use BigDecimal to handle the higher-order calculations, we can downgrade the decimal precision in outbound double values to the view layer. Then we have the option of using locale-accurate test data, managing the precision in our tests as well.

Check it out for yourself…

If you want to spin these examples up yourself, you can clone my repo. Also, if you want to see this work in continuous integration, check out my article on running a Jenkins / Android build server on Docker.

Reference:

AnDevCon: Espresso Is Awesome, But Where Are It’s Edges?

For my presentation at AnDevCon SF 2016, I focused on how Espresso represents a fundamental change in how approach the process of shipping software that provably works on a mobile ecosystem that is constantly changing.

The feedback was overwhelmingly good, many people who stopped by the Perfecto booth before or after our talk came to me to discuss topics I raised. In other words, it did what I wanted, which was to provide value and strike up conversation about how to improve the Android UI testing process.

If you’re pressed for time, my slides can be found below or at:
bit.ly/espresso-edges-andevcon

Why Espresso: Unit vs. UI testing

This article differentiates unit tests, such as those written for jUnit, from UI tests in Espresso through both purpose and technical value.

What is Espresso?

Espresso is an automated UI testing framework for Android apps. They are scripts written in Java that simulate interactions with the app while it is running, either in an emulated environment or on a physical device.

Espresso tests are “instrumented”, which means that internal workings (context) about the app such as object names, runtime variables, and other symbolic information is made available to the tests. Using Android Debug Bridge (ADB) to provide runtime feedback between tools like Android Studio and the app as test activities are executed on the target device.

How is Espresso different than Unit Testing?

Unit tests focus on small portions of code (i.e. class-level methods) and provide basic validation that code is working as expected. Espresso tests provide basic validation that the UI is working as expected.

Early feedback from lots of tiny unit tests on each build help developers know when they just “broke” something by changing some other portion of code. Early means often, and often means that speed and reliability of these tests are crucial.

To that end, unit tests typically are hermetic and rely on stubs/mocks to stand in for dependencies. Antithetically, Espresso UI tests work through platform API which requires a runtime and device capabilities that are not faked. This provides more realistic feedback on code that might work at the unit level, but fails when chained together or during basic usability validations.

When should we write Unit Tests?

Always. You can figure out for yourself what total percent of lines of code are covered by unit tests, but this is a battle against low-level technical debt. How often do you want to be surprised when a change that seemingly had nothing to do with one piece of code breaks because expectations over what that code does weren’t spelled out in a way that could be exercised regularly?

That’s really what validation testing boils down to: are we communicating basic expectations about the things we’re about to ship? Unit is just at a very low level, but the same applies at the UI workflow level too.

When should we write UI tests?

Whenever you have a UI…that’s pretty obvious, right? People use an app, they don’t call your class methods in isolation with static data on emulators. Eventually you have to get real: simulate clicks, drags, gestures, network conditions, and platform upgrades because that’s how real people are using your user interface (a.k.a. your app).

How much UI testing you do is up to you, but it boils down to time cost. UI tests are often more complicated to write, though as we see with Espresso, a developer-focused syntax and fast execution speed goes a long way to reducing cultural friction to writing UI tests as part of “development complete”.

Sideline: API teams, you don’t get off so easily either. Your developer experience is your UI, the patterns by which your users call your endpoints, designed or otherwise, are equivalent to workflows. Equivalent to UI testing for app developers, holistic API tests that simulate known trends and expectations on your API will help you isolate breaking changes earlier and faster in your build cycles, it’s that simple.