Chapter 5 of 11
Stability, Synchronization, and Flakiness Control
Animations, network delays, and backgrounding can turn good tests into unreliable ones. Tame flakiness with smart waits, retries, and defensive coding patterns tailored to mobile.
Why Mobile Tests Flake Out
Why Mobile Tests Flake Out
Animations, network delays, and OS dialogs make mobile UI tests much more timing-sensitive than classic web tests. You will learn waits, retries, and logging in Java + Appium to keep tests stable and debuggable.
Flakiness Symptoms
Common symptoms: tests pass locally but fail in CI, steps sometimes click the wrong element or nothing, and failures appear after backgrounding or locking the device.
Root Causes
Key mobile causes: animations and transitions, asynchronous loading, system popups and alerts, resource contention on devices/CI, and app lifecycle changes when backgrounded.
Goal: Precise Synchronization
The goal is not just "wait longer". It is to synchronize precisely with app state: wait just enough, in the right places, with clear diagnostics when things still go wrong.
What You Will Learn
Next: compare implicit, explicit, and fluent waits; handle animations and loading; deal with popups; add retries and idempotent steps; and improve logging for flaky tests.
Implicit vs Explicit vs Fluent Waits (WebDriver/Appium 2026 View)
Implicit Wait
Implicit wait sets a global timeout for `findElement` calls. It can hide performance issues and interact badly with explicit waits. Many teams in 2026 keep it very low or at 0 and avoid relying on it.
Explicit Wait
Explicit waits use `WebDriverWait` to wait for specific conditions, like visibility or clickability. They are recommended for mobile tests because they target exact states and give clear failures.
Fluent Wait
Fluent waits extend explicit waits with custom polling intervals and exception lists. They help when mobile UIs are noisy, slow, or when elements flicker during animations.
Modern Recommendation
In 2026 best practice: keep implicit waits minimal, use explicit or fluent waits around dynamic elements, and centralize waits in Screen Objects instead of sprinkling timing hacks in tests.
Implementing Explicit and Fluent Waits in Java + Appium
Here is how to implement explicit and fluent waits in Java with Selenium 4 style APIs and Appium. Assume you already have an `AppiumDriver<MobileElement>` or `AppiumDriver<WebElement>`.
Handling Asynchronous UI, Animations, and Loading States
Wait on State, Not Time
Avoid `Thread.sleep`. Instead wait for states: spinner invisible, button enabled, list has N items, or an animation flag gone. This aligns your test with real UI readiness.
Stable Anchors
Pick a stable element as a "screen ready" signal, like a title or primary button. Often you combine conditions: title visible AND loading indicator gone.
Avoid Interacting During Animations
In modern frameworks, elements can be present but moving. If clicks are flaky, wait for clickability or a non-animated container before interacting.
Scrolling and Lazy Lists
Lazy-loaded lists need scroll-and-check loops. Scroll a bit, look for the element, stop if found or if you reach the end, with an overall timeout.
Backgrounding and Resume
After backgrounding or device lock, always wait for an element that proves the app has resumed, using a helper like `waitForAppResumed()`.
Defensive Patterns for Popups, Permissions, and System Alerts
Popup Flakiness
Permissions and system alerts cause major flakiness: first runs show dialogs, later runs do not; OS alerts appear randomly; CI devices differ from local ones.
Centralize Handling
Create a `SystemDialogs` helper to accept or dismiss permission dialogs, update prompts, and generic alerts, instead of scattering this logic across tests.
Try-and-Ignore Logic
Use short explicit waits in try blocks: if the dialog appears, handle it; if not, continue. This makes the handler idempotent and safe to call often.
Device Setup and OS Differences
Pre-configure CI devices with needed permissions, but still keep defensive code. Handle Android and iOS differences explicitly via IDs and accessibility labels.
Retry Patterns and Idempotent Test Steps
This example demonstrates two ideas:
1) A reusable permission dialog handler that is safe to call multiple times.
2) A simple retry wrapper for a fragile action like tapping a button that may be briefly covered by an animation.
Thought Exercise: Making a Test Step Idempotent
Imagine you have a test step that taps a "Submit" button to send a form. Sometimes the network is slow, and the button remains enabled for a few seconds, so a retry might accidentally submit twice.
Task:
- In your own words, describe how you would make this "Submit" step idempotent (safe to call more than once) using waits and checks.
- Consider:
- What UI state proves the form is already submitted?
- How can you wait for that state before deciding to click again?
- What logging would you add to understand when retries happen?
Write down a short pseudo-algorithm like:
- Check for confirmation label.
- If present, log and return.
- Else, click submit and wait for confirmation.
Then compare your idea to a real implementation in your course project or lab code.
Debugging and Logging for Flaky Mobile Tests
Log Around Actions and Waits
Log before and after key actions and waits. Include locator, timeout, and outcome. Use prefixes like `[STEP]`, `[WAIT]`, `[INFO]`, `[ERROR]` for quick scanning.
Capture Evidence
On failure, capture screenshots and page source. Attach them to CI test reports so you can see what was on screen and which elements existed at failure time.
Meaningful Timeouts
Prefer several shorter, specific waits over one huge generic timeout. When a wait fails, you should know exactly which expected state did not appear.
Tag Flaky Areas and Keep Tests Small
Tag tests that rely on unstable dependencies, and prefer shorter, focused tests. This makes it easier to isolate and fix flakiness over time.
Check Your Understanding: Waits and Flakiness
Answer this question to test your understanding of waits and flakiness control.
You have a test where a button is present in the view hierarchy immediately, but is sometimes not yet tappable because of an animation. What is the BEST fix?
- Add Thread.sleep(5000) before tapping the button.
- Increase the global implicit wait from 5s to 60s.
- Use an explicit wait for the button to be clickable, and avoid long global implicit waits.
- Wrap the tap in a try/catch and ignore any exceptions.
Show Answer
Answer: C) Use an explicit wait for the button to be clickable, and avoid long global implicit waits.
Using an explicit wait for clickability synchronizes the test with the UI state (animation finished, element interactable) without slowing down other steps. Long implicit waits and Thread.sleep create unpredictable timing and slow tests, while ignoring exceptions can hide real issues.
Review Key Terms
Flip through these cards to reinforce the core concepts of stability and flakiness control.
- Implicit wait
- A global timeout for element lookups in WebDriver/Appium. It tells the driver how long to poll for elements before throwing an error, but can interact badly with explicit waits if set too high.
- Explicit wait
- A targeted wait (e.g., WebDriverWait) for a specific condition like visibility or clickability of an element. Preferred for handling dynamic mobile UI states.
- Fluent wait
- A configurable form of explicit wait that lets you set timeout, polling interval, and ignored exceptions, useful for noisy or slow mobile UIs.
- Idempotent test step
- A step that can be executed multiple times without changing the final outcome, often implemented by checking UI state before acting and after retries.
- Flaky test
- A test that sometimes passes and sometimes fails without code changes, often due to timing, async operations, or environmental issues.
- Synchronization
- Aligning test actions with the actual state of the app (e.g., waiting for loading to finish) so that operations occur when the UI is ready.
Key Terms
- Flaky test
- A test whose result is unreliable, sometimes passing and sometimes failing without any relevant code changes.
- Fluent wait
- A specialized explicit wait that allows configuration of timeout, polling interval, and which exceptions to ignore while waiting.
- System popup
- An operating system-level dialog, such as permission requests or alerts, that appears on top of the app and can block interactions.
- Explicit wait
- A wait that targets a specific condition (such as an element becoming visible or clickable) using classes like WebDriverWait.
- Implicit wait
- A global timeout that applies to all element-finding operations in WebDriver/Appium, causing the driver to poll for elements up to the specified time.
- Loading state
- A temporary UI state showing that data is being fetched or processed, often indicated by spinners, progress bars, or skeleton screens.
- Idempotent step
- A test action that can be executed multiple times but produces the same final state, often by checking conditions before acting.
- Synchronization
- The practice of coordinating test actions with the application's real-time state to avoid timing-related failures.