🎯 Perfecting Your Jetpack Compose Skills with a Bookmark Button Project 🏆

Photo by Chiara F on Unsplash
Can you create this piece of UI?

Let’s say the above piece of UI is today’s task from the planning session, Sounds quite easy, right? Maybe under 10 minutes, we can code a @Composable function?

Here’s a small breakdown of what we’re asked to build:

A bookmark button that has 3 states:

  • Bookmarked 📌
  • Not bookmarked ❌
  • Toggling 🔄

The following requirements could be identified from the gif above:

based on each of these states the UI changes,

state changes should be communicated to the backend

while toggling the button should not be clickable

How would you, as an Android Engineer, implement it?

How about the following?

find the code for this part in this commit:

https://github.com/LloydBlv/BookmarkButton/commit/8310ba9be9da0cd237c4c3cea5ddda2f396ead30

Which results in a simplified version of the design requirement:

Let’s say the design is good enough for the first version of our app and we’ve covered all the requirements and the only thing left is:

while toggling the button should not be clickable

Let’s quickly implement this:

with a one-liner, we could cover that requirement.

So, it seems like there’s not much to do, and we’ve already implemented what we needed! But, let’s see what other things we can explore from here?

Let’s quickly enable Compose metrics to see how we are doing regarding performance.

https://github.com/LloydBlv/BookmarkButton/commit/3eb6ab89c1050f98044366edde41ec67255ec091

Seems like we’re doing pretty well though, the composable we’ve created is both restartable & skippable and all arguments are marked as stable.

Let’s say we also care about the code readability (we should, right?) and we do a quick refactoring:

We could aggregate two boolean flags and create a state class, we could leverage Kotlin’s sealed interface to achieve better code readability.

It seems like it, but there is much more we could do, and in my opinion, we should do and care about them.

Config changes are usually underrated, and they usually cause weird crashes and unexpected states in the app. Let’s see how our button does after a configuration change.

State is lost after configuration change

Here’s what’s happening:

Multiple configuration changes are triggered using the script below:

As we can see, the bookmarked state gets lost after any configuration change. Converting remember to rememberSaveable will fix this issue for us, and the last state of the bookmark will be stored/restored after any configuration changes.

Many developers stop here, and this code makes its way to production, but wait, there’s a big missing part in all this!🛑🤯

You’ve probably noticed that we’ve written no tests at all for our feature. This code can make its way to production, and we most probably will be happy with it for a while, but as they say,

“Change is the only constant.”

Sooner or later, some more requirements will come in, and we’d be asked to change something in this code or around it. We can definitely make those changes without having any tests, but how can we make sure we’re not breaking anything while doing those changes?

We can do manual testing, a good amount of manual testing, but again, what’s the guarantee that we have tested every scenario?🤔💔

For more information For more information about testing, check out the Android Developers site.

Let’s start with some unit tests for these piece of UI,

The above image shows the structure of tests in a newly created (Or a project with no tests 🤦).

Tests setup alongside some tests has been added in the following commit:

https://github.com/LloydBlv/BookmarkButton/commit/616ecb9890be11735e18c5ba6fc4464fe735b823

Tests above ensure that each state of our bookmark button is set correctly as we expected, given the product requirements. 🎯📊

But we’re NOT done yet; we have not tested the transition between these states. We should make sure when the state is toggling, the progress bar is shown and disappears correctly. 🔄🛠️

So far we’ve come up with following tests:

These tests ensure a more robust product, especially behavior-wise. But how can we make sure our UI remains robust throughout different feature implementations in a team or organization? 🤝🔍

Screenshot testing simply means taking screenshots/snapshots of different pieces of UI in an Android application and later verifying that piece of UI results in the same screenshots. If not, git hooks/CI pipelines could fail to notify about some unexpected changes in the current PR. 🚨🔧

for more information about screenshot testing:

As the above link, there are many screenshot testing libraries, We will be using Roborazzi for this article:

By having the above implementation, we can generate base/gold screenshots for our bookmark button in different states. These screenshots will be verified in our CI pipeline every time a PR/MR is raised, ensuring our UI’s integrity. 🖼️👍

Screenshots generated based on different states of our button

The following images are generated and committed to our codebase:

Source link