What Unit Tests are Trying to Tell us about Activities: Pt. 1

2017-02-25

Activitys and Fragments, perhaps by some strange historical accidents, have been seen as the optimal building blocks upon which we can build our Android applications for much of the time that Android has been around. Let’s call this idea – the idea that Activitys and Fragments are the best building blocks for our apps – “android-centric” architecture.

This series of posts is about the connection between the testability of android-centric architecture and the other problems that are now leading Android developers to reject it; it’s about how our unit tests are trying to tell us that Activitys and Fragments don’t make the best building blocks for our apps because they force us to write code with tight coupling and low cohesion.

In this first part of the series, I want to say a little about why I think android-centric architecture has been dominant for so long and to provide a little background on why I think unit tests have insightful things to say about rejecting android-centric architecture.

What is Android-Centric Architecture?

An android-centric architecture is one in which each screen the user sees is ultimately backed by a class whose main purpose is to interact with the android operating system. As we’ll see later, Diane Hackborne and Chet Haase have both recently stated that Activitys are an example of such a class. Since Fragments are very similar to Activitys, I consider an app where each screen is backed by a Fragment to also have an android-centric architecture, even if there’s only one Activity in the app.

MVP and VIPER and RIBLETS and…are a thing now in the Android community. However, these suggestions aren’t necessarily a full rejection of android-centric architecture. Although there may be Presenters or Interactorss or whatever involved, these objects are often still built on top of Activitys and Fragments; they could still get instantiated by and delegate to android-centric components, one for each screen the user sees.

An app that doesn’t follow android-centric architecture has one Activity and no Fragments. Router and Controller type classes are POJOs.

Why Android-Centric Architecture?

I suspect that a part of the reason why we buy into android-centric architecture is that Google hasn’t really been clear on what Activitys and Fragments are for until relatively recently. On channels less official and visible than the Android docs, Chet Haase and Diane Hackborne have both suggested that Activitys aren’t really the kind of things with which you want to build your application.

Here’s Hackborne:

…With its Java language APIs and fairly high-level concepts, it can look like a typical application framework that is there to say how applications should be doing their work. But for the most part, it is not.

It is probably better to call the core Android APIs a “system framework.” For the most part, the platform APIs we provide are there to define how an application interacts with the operating system; but for anything going on purely within the app, these APIs are often just not relevant.

and here’s Haase:

Application components (activities, services, providers, receivers) are interfaces for your application to interact with the operating system; don’t take them as a recommendation of the facilities you should architect your entire application around.

Hackborne and Haase almost explicitly reject android-centric architecture. I say “almost”, as they both don’t seem to denounce the use of Fragments as building blocks for our apps. However, there’s a tension between the idea Activitys are not suitable app components and that Fragments are, and that tension is as strong as the the many similarities between the two components.

It might even be fair to say that Google has actually suggested an android-centric architecture through the previous Google I/O app samples and the android documentation. The “app components” section of the Android docs is a particularly good example of this. The section introduction tells the reader that they’ll learn “how you can build the components [including Activitys and Fragments] that define the building blocks of your app.”

Over the past couple of years, many Android developers – myself included – are starting to realize that Activitys and Fragments often are not helpful building blocks for their applications. Companies like Square, Lyft, and Uber are moving away from android-centric architecture. Two common complaints stand out: as the app gets more complicated, the code is difficult to understand and too rigid to handle their varying use-cases.

What does Testing have to do with this?

The connection between testability and understandable, flexible code is well expressed in this quotation from Growing Object Oriented Software Guided by Tests:

for a class to be easy to unit-test, the class must…be loosely coupled and highly cohesive – in other words, well-designed.

Coupling and cohesion have direct bearing on how understandable and flexible your code is, so if this quote is right and if unit testing Activitys and Fragments is difficult – and you likely know that even if you haven’t read my posts suggesting as much – then writing unit tests would have shown us, before Google and painful experiences did, that Activitys and Fragments aren’t the building blocks we want for constructing our applications.

Next Time…

In the next post, I’ll try and fail to write an example test against an Activity and show exactly how the tight coupling and low cohesion of Activitys makes testing difficult. Next, I’ll test drive the same functionality, and we’ll end up with testable code. In the following post, I’ll show how the resulting code is loosely coupled and highly cohesive and talk about some of the benefits of these properties, including how they open up novel solutions to common problems on Android, like runtime permissions and intermittent connectivity.


Image Credit: Maret Hosemann, “Der Bruch,” Creative Commons 2.0

androidtestingarchitecture

Some Resources for Learning how to Test Android Apps

Towards Godless Android Development: How and Why I Kill God Objects

comments powered by Disqus