Sergio Sastre Florez
Sergio's little tech corner

Sergio's little tech corner

Design a pixel perfect Android app 🎨

Design a pixel perfect Android app 🎨

A story of UI/UX and screenshot testing

Sergio Sastre Florez
·Mar 30, 2022·

11 min read

Featured on Hashnode

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

AndroidWeekly The success of your app depends heavily on its UI/UX.

The more popular your app becomes, the more you should care about the diversity of your users. They might speak different languages, or suffer from some sort of visual impairment, or feel too much eye strain when using the app under low light conditions, etc. Thus, all these factors together influence the design of your app, how you satisfy the expectations of different groups of users. Making your app pixel perfect for all of them takes effort.

But that's when screenshot tests come to the rescue: they do not only help automate the verification of the UI correctness, but they're also fast and simple to write, at least on Android.

With screenshot testing, you can easily verify your Android app under different configurations, i.e. user preferences, for instance:

  • Locales
  • Font sizes
  • Orientations
  • UI modes (also known as Dark/Light mode or Day/Night mode)

That's why, in this blog post, you will learn:

  1. why it is important to test your UI under such configurations.
  2. which libraries you can use to automate its verification with parameterized screenshot tests. No more manual testing. No more back and forth between the app and the settings.

Why testing multiple configurations matter

Locale

By localizing your app, you are making your app fit the local market conditions, lowering the cultural and language barrier for new potential customers.
Nevertheless, localizing applications is more challenging than it looks at first. Translating text itself is challenging. For instance, one word like “clear” in English could correspond to two completely different words in another language (“clear” as in “transparent”, or as in “delete”). The word needs context.
But there are many other factors to take into account:

  • RTL languages

    • Text alignment & layout direction. In general, most layouts and texts should be aligned right to left instead of left to right.
    • Drawable mirroring. At least, any icon indicating direction also needs mirroring to be consistent with the text flow ( e.g. icon '' becomes ''). Same goes for animations.
  • Numerals: in some locations, they use numeral systems you are not used to. The most common and conventional are the Western and Eastern Arabic numerals, but there are many other numeral systems. Use the one your users are used to.

Numeral systemNumerals
Western Arabic numerals0 1 2 3 4 5 6 7 8 9
Eastern Arabic numerals٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩

The list of things to take into account is very long, though: plurals, date formats, currency formats, punctuation signs for decimals, measure units, phone numbers, etc. Keep all that in mind.

Pseudolocales

If our app supports more than a handful of languages, testing app localization for every single language becomes extremely time-consuming.

You can use pseudolocales instead of the full set of supported languages to mitigate that problem.
Pseudolocales are locales that are designed to simulate characteristics of languages that cause UI, layout, and other translation-related problems when an app is translated. As stated in the Android developers documentation 1, Android supports the following pseudolocales:

  1. en_XA:
    • Adds Latin accents to the base English UI text.
    • Expands the original text by adding non-accented text.
    • Brackets each message unit to expose potential issues from expanded text.
  2. ar_XB:
    • Sets the text direction of the original left-to-right messages to the right-to-left direction, which reverses the order of the characters in the original message.
    • On API < 28, it also uses Eastern Arabic numerals when displaying numbers. This is not documented though.

Spot localization issues

If supporting LTR and RTL languages, I recommend verifying your UI under at least one language for each group. For example, for an LTR language

en_locale.png

Locale en:
✅ Everything looks fine


If you're planning to support RTL-languages in the future, use ar_XB. It helps spot issues without the need to understand any of those languages (texts are mirrored), as depicted below.

ar_xb_locale.png

Locale ar_XB:

  • Numerals are localized (i.e. Eastern Arabic Numerals for < API 28).
  • Layouts and texts are mirrored.
  • Training icon doesn't need mirroring (it doesn't imply direction).

    ✅ Everything looks fine


Some languages, like German, tend to be up to 30% more verbose than the average! You can identify UI issues on verbose languages by setting the Locale to en_XA. en_XA_locale.png

Locale en_XA:

  • Texts become more verbose.

    ⚠️ Views get overlapped & text cuts off (missing opening bracket in overlapping text).

You've spotted an issue with view constraints!

Font size

The Android system comes with special settings to improve the UX for users with vision impairments. One of those settings is the font size, to enhance readability. It enables users to increase the system font size to use their phones without holding them too close to their eyes.
In an app I was working in the past, we were tracking the font size setting of our users. The results were unexpected:

Font_size_statistics.png

Data from a multi-banking app with 80.000 monthly active users in Germany in 2018 2.

Almost 30% (i.e. 1/4) of our users used a font size different from the default one! and more than 20% had it set to large/largest! It seems it's worth checking out what the app looks like under those settings.

Spot font size issues

If you are a developer without any visual impairment, you're likely testing your UI-related code with the default device or emulator font size. Thus, any text-related issue is quickly identified and fixed... apparently.

normal_font_size.png

Normal font size:
✅ Everything looks fine

Nonetheless, your app probably has users with visual impairments as well. The odds are high that some of them use large or largest font sizes, what makes UI elements with text taller and larger. This might result into overlapping of UI components, text being cut-off and the like.

largest_font_size.png

Largest font size:

  • Texts become taller and larger.

    ⚠️ Views with text look suffocated (missing padding, one word "Delete" text split into 2 lines).

A pixel perfect app should mind those users too.

Pseudolocale en_XA vs. large & largest font sizes

Keep this in mind:

  • The pseudolocale en_XA makes the same text more verbose, and therefore larger.
  • Large & largest font sizes make the same text not only larger, but taller as well.

Orientation

Best designed mobile apps optimize their layouts to make the most out of the available space on the screen.
This means that designers have to account for the extra horizontal space and the reduced vertical space in landscape orientation. As a consequence, some layouts might need some UI realignment or even a brand-new redesign in landscape!

Spot orientation issues

The main goal is to identify potential design improvements to better the UX in landscape orientation.
For instance, RecyclerViews might contain rows that are tall in portrait orientation. However, the screen height is greater than its width, and tall rows are fully visible in portrait orientation in most cases.

portrait.png

Portrait orientation:
✅ Everything looks fine

But the screen height is usually much lower in landscape orientation. That means, the very same tall row takes up a much bigger percentage of the screen height in landscape. This results in many less visible rows on the screen. Although that is unavoidable, you must strive to reduce the row height in landscape orientation: if rows are less tall -> more rows visible -> users need to scroll less.

landscape.png

Landscape orientation:

  • Same design as in portrait mode

    ⚠️ Same height as in portrait mode but the text body takes much more space than necessary

You've spotted a potential design improvement in landscape orientation!

You could enhance its design in landscape orientation by moving UI components around to reduce the row's height, taking advantage of the extra screen width in landscape orientation. For example, you can place the image fully inside the CardView and push the flag, icon and text underneath to the right of the image. In doing so, you reduce its height because the image is not going beyond the CardView limits anymore.

landscape optimized.png

Landscape orientation (optimized layout):

  • 15% less tall than in portrait orientation while showing the same info.

    ✅ Minimized row height

UI mode

In order to optimize the UX at night, some apps come with the so-called night/dark mode. This UI mode aims to reduce the eye strain caused by the light emitted by device screens, resulting in a more pleasant experience in general. This is done by maintaining the minimum colour contrast ratios required for readability.

Spot UI mode issues

Regarding UI modes, you want to ensure that the UI components play together in harmony. So try to avoid contrast issues that might hinder reading or cause eye strain, in light mode as well as in dark mode.

default_dark_mode.png

Light/Dark mode:

  • Same colours but the cardView has different background colour.

    ⚠️ The red tone is saturated in dark mode and vibrates against a dark surface.

Material design recommends lighter tones (200-50) in dark mode, rather than default color themes (saturated tones ranging from 900-500). You can use the recommended lighter red tone in dark mode by Material design (i.e. #CF6679) as well as a dark colour for the background of the call-to-action bar, as follows

optimized_dark_mode.png

Light/Dark mode (optimized):

  • Lighter red tone in dark mode.
  • Dark colour for the background of the call-to-action bar.

    ✅ Good readability in dark mode.

Screenshot test'em all!

One test to rule them all

In the previous sections, you've seen how important is to test your design against multiple configurations. Nevertheless, testing every settings combination manually looks like a nightmare...

But what if you could automate it? If you could write one screenshot test and run it under all desired configurations with parameterized tests? That's the idea behind parameterized screenshot testing!

Testing under different configurations

There are already plenty of good libraries for snapshot testing on Android nowadays.
cashApp/Paparazzi 3 and shopify/android-testify 4 screenshot testing libraries provide some out-of-the-box support for snapshot testing under different configurations.

However, Facebook-screenshot-testing 5 and pedrovgs/Shot 6 libraries, which are the most popular as of March 2022, do not. That is also the case if you're not using any library, but a custom solution for snapshot testing or simply taking screenshots while running your UI tests.

But fear not. You can still do it by using them together with sergio-sastre/AndroidUiTestingUtils 7 in your instrumented tests. The library is based on ActivityScenarios and TestRules to set all the configurations discussed in this blog post. It shows some examples of usage to snapshot test Views, Activities or Composables in its ReadMe.

Parameterization

The most flexible library to write parameterized snapshot tests is google/TestParameterInjector 8. cashApp/Paparazzi examples use it. If you search for examples with pedrovgs/Shot, take a look at Road-To-Effective-Snapshot-Testing 9. You can use google/TestParameterInjector with any other screenshot testing library the same way.

Do you want to try it on your own?

You will find all the examples used in this blog post and more, together with their corresponding parameterized snapshot tests and other good practices for effective snapshot testing in my Road-To-Effective-Snapshot-Testing repo.

Conclusion

Making sure that your app provides the best UX to all groups of users will put you ahead of your competition.
But there are several configurations that influence how the content is perceived by the user, and considering all of them looks overwhelming at first glance.

Therefore, you've learnt, first of all, what to look for when verifying your UI under multiple configurations. Secondly, you've seen there are many tools to automate this process with parameterized screenshot tests: just one screenshot test and different configuration data is enough to test it all!

From quickly spotting visual bugs to ensuring that code changes do not break your pixel perfect app, screenshot testing can help you with all that.


Special thanks to Pablo Gallego for reviewing this blog post! If you are interested in the content I publish, follow me on Twitter!

References

1 Android developers: Test your app with pseudolocales
2 Font size info pie chart: taken from my tech-talk "An introduction to effective snapshot testing on Android". You can find the videos and the slided under the following links:

3 cashApp/Paparazzi: screenshot testing without physical devices or emulators
4 shopify/android-testify: screenshot testing with Android Studio Plugin support
5 Facebook-screenshot-testing: First screenshot testing library on Android
6 pedrovgs/Shot: screenshot testing with anti-flakiness-mechanisms and handy snap view methods
7 sergio-sastre/AndroidUiTestingUtils: library to configure your Activities/Views/Composables under different configurations via ActivityScenarios and TestRules.
8 google/TestParameterInjector: a Junit4 and Junit5 test runner that supports parameterized test in a flexible means.
9 Road-To-Effective-Snapshot-Testing: project containing screenshot tests for all the examples in this blog post and more, as well as additional tips for snapshot testing effectively

Further Reading

You can find the other articles on snapshot testing of this series here!

  1. An introduction to snapshot testing on Android in 2021
  2. The secrets of effectively snapshot testing on Android
  3. UI tests vs. snapshot tests on Android: which one should I write? 🤔
 
Share this