Alternatives to Idling Resources in Compose tests | by Jose Alcérreca | Android Developers | Apr, 2022

Alternatives to Idling Resources in Compose tests | by Jose Alcérreca | Android Developers | Apr, 2022

Android Flow Chart

In this article you’ll learn how to use the waitUntil test API in Compose to wait for certain conditions to be met. This is a good alternative to using Idling Resources… in some situations.

One way to categorize tests is by their scope. Small tests, or unit tests, focus on small pieces of your app while big tests, or end-to-end, cover a large portion of your app. You can read about this and other types of tests in the newly updated testing documentation.

Different test scopes in an app

Synchronization is the mechanism that lets the test know when to run the next operation. The bigger the chunk of code you choose to verify, the harder it is to synchronize with the test. In unit tests it’s easy to have full control of the execution of the code to verify. However, as we grow the scope to include more classes, modules and layers, it gets tricky for the test framework to know if the app is in the middle of an operation or not.

Correct synchronization between test and app

androidx.test and, by extension, Compose Test, use some tricks under the hood so that you don’t have to worry too much about this. For example, if the main thread is busy, the test pauses until it can execute the next line.

However, they can’t know everything. For example, if you load data in a background thread, the test framework might execute the next operation too soon, making your test fail. The worst situation is when this happens only a small percentage of the time, making the test flaky.

Idling Resources are an Espresso feature that lets you, the developer, decide when the app is busy. You have two ways to use them:

1. Installing them in the framework or library that is doing work that the test can’t see.

A good example of this is RxIdler, which wraps an RxJava scheduler. This is the preferred way to register Idling Resources because it lets you keep your test setup cleanly split from the test code.

2. Modifying your code under test to explicitly expose information about whether your app is busy or not.

For example, you could modify your repository (or a test double) to indicate that is busy while loading data from a data source:

This is not ideal because you’re polluting your production code, or creating complicated test doubles, and there are some situations when they’re hard to install. For example, how would you use Idling Resources in a Kotlin Flow? Which update is the final one?

Instead, we can wait for things.

Loading data is usually fast, especially when using fake data, so why waste time with idling resources when you can just make the test sleep for a couple of seconds?

This test will either run slower than needed or fail. When you have hundreds or thousands of UI tests, you want tests to be as fast as possible.

Also, sometimes emulators or devices misbehave and jank, making that operation take a bit longer than those 2000ms, breaking your build. When you have hundreds of tests this becomes a huge issue.

If you don’t want to modify your code under test to expose when it’s busy, another option is to wait until a certain condition is met, instead of waiting for an arbitrary amount of time.

In Compose, you can leverage the waitUntil function, which takes another function that produces a boolean.

Of course we can make a function that takes any matcher and hides the boilerplate:

…add some more sugar to make the test wait until something exists or stops existing…

…and use it like this:

This should be used as a last resort when installing an Idling Resource is not practical or you don’t want to modify your production code. Using it before every test statement should be considered a smell, as it pollutes the test code unnecessarily, making it harder to maintain.

When should you use it then? A good use case for it is loading data from an observable (with LiveData, Kotlin Flow or RxJava). When your UI needs to receive multiple updates before you consider it idle, you might want to simplify synchronization using waitUntil.

For example, when you collect a Flow from a view:

And you emit multiple items to it:

If repository takes an indeterminate amount of time to come back with the first result, the test framework will think “Loading” is the idle state (the initial value assigned in collectAsState) and continue with the next statement.

So, you can make the test much more reliable if you make sure the UI is not showing the loading indicator:

Source link

Leave a reply

Please enter your comment!
Please enter your name here