
Product news and testing tips.
Qualflare ingests Android test results from Espresso, iOS results from XCTest/XCUITest, and cross-platform UI results from Maestro — all through JUnit-XML-compatible ingestion — then applies the same flaky detection, failure clustering, and release-risk scoring it applies to every other framework. It is a mobile test results and observability layer, not a device-execution cloud: it does not provision real devices or simulators, and it does not run your tests.
Key takeaways
- Qualflare is a mobile test results/observability layer, not a device-execution cloud — it analyzes results after they run, it doesn't provision devices or run tests.
- Espresso emits JUnit-XML natively via Gradle; XCTest/XCUITest needs .xcresult-to-JUnit-XML conversion; Maestro emits JUnit-XML natively via `maestro test --format junit`.
- Mobile build flakiness is rising fast: the share of teams hitting flakiness grew from 10% in 2022 to 26% in 2025, across 10M+ Bitrise builds.
- Appium has no official JUnit reporter — what's actually ingestible is the JUnit, TestNG, or pytest harness wrapping the Appium suite.
- Device clouds (BrowserStack, Sauce Labs, Firebase Test Lab) run your tests on real hardware; observability platforms (Qualflare, TestRail, Allure TestOps) analyze what those runs produced. Most mature mobile teams need both.
- Teams using monitoring/observability tooling see roughly 25% fewer flaky reruns than teams that don't, per the same Bitrise analysis.
Qualflare ingests Android test results from Espresso (native JUnit-XML via Gradle), iOS results from XCTest/XCUITest (converted from .xcresult to JUnit-XML), and cross-platform UI results from Maestro (native JUnit-XML via maestro test --format junit) — then applies the same flaky-test detection, failure clustering, and release-risk scoring it applies to every other framework it ingests. Qualflare is not a device-execution cloud. It does not provision real devices, emulators, or simulators, and it does not run your test suite. That job belongs to BrowserStack, Sauce Labs, Firebase Test Lab, HeadSpin, or Kobiton — tools that solve a different, earlier problem in the pipeline.
This distinction is the single most important thing to understand before evaluating any “mobile testing tool,” because the category is really three separate categories wearing one label. This guide is the map: what each layer does, where Qualflare fits, how Android and iOS test results actually get from a device to a dashboard, and where mobile testing gets harder than web or backend testing. Where we reference Qualflare, our own product, we stick to capabilities we can verify — the same standard we hold every vendor to.
The three layers of “mobile testing,” at a glance
Before frameworks, CI, or tools — the fastest way to place any mobile testing product is to ask which of these three jobs it does. Almost every point of confusion in this space (and most of the “does X do Y” support questions we get) traces back to conflating these rows.
| Category | What It Solves | Examples | Best For | Not For |
|---|---|---|---|---|
| Mobile test frameworks | Writing and running mobile tests against an app | Espresso, XCUITest/XCTest, Appium, Maestro, Detox | Authoring test cases and executing them against a build | Analyzing results after the run, or provisioning devices |
| Device-execution clouds | Provisioning real devices and emulators to run tests on | BrowserStack, Sauce Labs, Firebase Test Lab, HeadSpin, Kobiton | Running a suite across many real device/OS/screen combinations | Flaky-test detection, failure clustering, or release-readiness scoring |
| Mobile test management / observability | Analyzing results after they run | Qualflare, TestRail, Zephyr, Allure TestOps, ReportPortal, Testmo | Flaky detection, failure clustering, trend analysis, release readiness | Writing test code or provisioning device hardware |
Qualflare sits entirely in the third row. It doesn’t compete with Espresso or XCTest (you still write and run your tests with them), and it doesn’t compete with BrowserStack or Firebase Test Lab (you still need something to execute across real hardware if your matrix requires it). It’s the layer that turns whatever results those tools produce into flaky scores, root-cause clusters, and a release-risk verdict — the same layer test observability already provides for web and backend suites, extended to mobile result formats.
What “mobile testing” actually covers
“Mobile testing” spans more ground than most teams initially scope for. At minimum it includes: unit tests running on the JVM or in an iOS test bundle, UI/instrumented tests that drive the app on a device or emulator, and end-to-end flows that exercise a full user journey — sign-up, checkout, permissions, deep links — across real hardware and OS versions. It spans two native platforms (Android, iOS) with genuinely different toolchains, plus a growing share of cross-platform apps (React Native, Flutter) that add a third layer of indirection between the test and the platform it ultimately runs on.
That breadth is why mobile testing earns its own content pillar here rather than folding into general CI testing advice. A JUnit-XML file from a pytest suite and a JUnit-XML file converted from an .xcresult bundle look identical to a results dashboard — but getting to that file, and what typically breaks along the way, are completely different problems on each platform.
What is mobile test observability?
Mobile test observability is the same discipline as general test observability — understanding why tests pass or fail over time by analyzing results across every run, not judging a single run in isolation — applied to Android and iOS result data specifically. It answers questions a per-run dashboard can’t: Is this Espresso test flaky, or did the last commit really break it? Do these twelve failing XCUITest cases share one root cause? Is this build safe to ship to the App Store, or is it trending riskier than the last release?
The reason mobile needs its own treatment isn’t a different definition — it’s a different data shape. Android and iOS tests fail for platform-specific reasons a generic CI dashboard doesn’t surface: animation timing, permission-dialog races, provisioning-profile mismatches, emulator cold-start flake, App Transport Security quirks. A results layer that’s mobile-aware can group and explain those failures instead of presenting them as an undifferentiated wall of red. We cover this in full depth in What Is Mobile Test Observability?, and the practical side of getting Android and iOS results into one place is covered in One Dashboard for Espresso, XCTest & Maestro.
Flaky mobile tests: a category of their own
Flakiness is worse on mobile than almost anywhere else in the stack, and it’s getting worse, not better. Bitrise’s analysis of 10M+ mobile builds from January 2022 to June 2025 found the share of teams experiencing build flakiness rose from 10% in 2022 to 26% in 2025 — a two-and-a-half-times increase in three years, on the platform’s own build data. The same report found teams that adopted monitoring/observability tooling saw roughly 25% fewer flaky reruns than teams that didn’t, alongside higher build success rates overall — direct evidence that the analysis layer, not just better test code, moves the number.
Mobile tests are flakier by construction. A web end-to-end test depends on a browser and a network call; a mobile UI test depends on all of that plus a device or emulator boot cycle, OS-level permission and notification dialogs, animation and transition timing, GPS/sensor simulation, background-process throttling, and — on real-device clouds — physical hardware contention shared across other tenants’ test runs. Every one of those is a source of non-determinism that a unit test never touches. The general playbook — detect from historical pass/fail data, quarantine to stay unblocked, fix root causes, don’t just retry forever — still applies; see the complete guide to flaky tests for the underlying mechanics. What’s different on mobile is the taxonomy of causes and the framework-specific fixes, which we cover at length in Flaky Mobile Tests: Why Android & iOS Tests Fail Randomly.
The mobile testing frameworks landscape
Four frameworks dominate mobile test authoring today, and each one has a different relationship with JUnit-XML — the format most result-ingestion pipelines, Qualflare’s included, standardize on.
Espresso is Google’s native Android UI-testing framework. It runs through AndroidJUnitRunner, which is itself a JUnit 4 test runner, so instrumented test runs already produce standard JUnit-XML output the moment Gradle finishes — no converter, no plugin. Running ./gradlew connectedAndroidTest writes XML results straight to build/outputs/androidTest-results/connected/ alongside an HTML report, per Android’s own command-line testing documentation. Of the four frameworks here, Espresso has the shortest path to any JUnit-XML-based tool.
XCTest and XCUITest are Apple’s native testing frameworks — XCTest for unit tests, XCUITest (built on top of it) for UI automation — both run through Xcode and xcodebuild. The catch: Xcode doesn’t write JUnit-XML. It writes a binary .xcresult bundle, which has to be converted before any JUnit-XML-based tool can read it. For years the default converter was fastlane-community/trainer, but it’s effectively dead — its GitHub repo shows no release since September 2019 and no commit since November 2021. The currently maintained option is a7ex/xcresultparser, which parses the .xcresult bundle and emits JUnit-XML (plus Cobertura/Sonar-style coverage reports) and continues to ship releases. If your pipeline is still pinned to trainer, that’s worth revisiting — an unmaintained converter is a silent single point of failure the moment an Xcode update changes the .xcresult schema underneath it.
Maestro is a newer, YAML-based UI-testing tool for both Android and iOS that’s grown quickly because it needs no boilerplate test code — flows are declarative. It’s also the one cross-platform mobile framework here with zero-friction JUnit-XML output: maestro test --format junit writes a standard JUnit-XML report directly, per Maestro’s CLI documentation. No conversion step, same as Espresso. See Maestro Mobile Testing: What It Is and How It Compares to Appium for a deeper look at where it fits.
Appium is the veteran of cross-platform mobile automation — a W3C WebDriver-based framework that drives both Android and iOS (and beyond) through one API. The detail that trips teams up: Appium itself has no official JUnit reporter. It’s an automation driver, not a test runner — what actually produces JUnit-XML is the harness wrapping your Appium calls. A suite written with JUnit or TestNG (common in Java/Kotlin Appium projects) or pytest (common in Python Appium projects) gets JUnit-XML for free from that harness, completely independent of Appium itself. “Does Qualflare support Appium?” is really “does Qualflare support the JUnit/TestNG/pytest reporter your Appium suite already uses?” — and the answer is yes, through those existing parsers. See Appium Flaky Tests: Root Causes and Stable Mobile Automation for its specific reliability patterns, and Espresso Flaky Tests / XCTest & XCUITest Flaky Tests for the native-framework equivalents.
For a full side-by-side of all four, see Appium vs Espresso vs XCUITest vs Maestro (2026).
Detox, the React Native gray-box testing framework, is worth naming even though it’s outside today’s ingestion path. It runs on Jest under the hood, and there’s no confirmed native way to get Detox/Jest-RN results into a JUnit-XML-based pipeline today. If your app is React Native, the practical route for automated UI coverage that is ingestible is Maestro, which drives the compiled app the same way regardless of what framework built it.
| Framework | Platform | Native test output | Path to JUnit-XML | Ingestible today |
|---|---|---|---|---|
| Espresso | Android | JUnit-XML (via AndroidJUnitRunner/Gradle) | None needed — automatic | Yes, natively |
| XCTest / XCUITest | iOS | .xcresult bundle (binary) | Convert with a7ex/xcresultparser | Yes, via converted JUnit-XML |
| Maestro | Android + iOS | JUnit-XML (maestro test --format junit) | None needed — automatic | Yes, natively |
| Appium | Android + iOS | None — no official reporter | Comes from the JUnit/TestNG/pytest harness wrapping it | Yes, if wrapped in JUnit/TestNG/pytest |
| Detox | React Native | Jest-based output | No confirmed native path | Indirectly only, via Maestro |
See the full framework support list for how this fits the other 20+ formats Qualflare’s CLI auto-detects, including the JUnit-XML catch-all that covers any framework emitting compatible XML.
Android vs iOS: where the testing differences actually bite
Beyond the JUnit-XML question, Android and iOS testing diverge enough that a team fluent in one platform’s CI setup often has to relearn the other from scratch. The toolchains share almost nothing upstream of the results file.
| Dimension | Android | iOS |
|---|---|---|
| Build tool | Gradle | Xcode / xcodebuild |
| Native language | Kotlin / Java | Swift / Objective-C |
| Native UI framework | Espresso, Jetpack Compose testing | XCTest / XCUITest |
| Native test output | JUnit-XML, automatically | .xcresult bundle, needs conversion |
| Emulator/simulator source | Android Emulator (many API levels) | Xcode Simulator (macOS-only) |
| CI runner requirement | Any Linux/macOS runner | macOS runner required |
| Common flake sources | Emulator cold start, animation timing, background-app throttling | Simulator boot time, provisioning-profile/signing issues, system-dialog races |
The practical consequence is that Android CI can run on cheap Linux runners while iOS CI is locked to (typically more expensive, often slower to provision) macOS runners — a cost and scheduling asymmetry that shapes how teams parallelize and shard their suites long before flakiness ever enters the picture. It also means the two platforms fail differently: an Android suite’s flakiest layer is usually emulator startup and animation timing, while an iOS suite’s flakiest layer is more often simulator boot time and code-signing edge cases in CI. A results layer that treats “mobile” as one undifferentiated bucket will miss that these are different problems requiring different fixes — which is exactly why unifying the dashboard (one view of pass rate and trend) has to stay separate from unifying the diagnosis (Android and iOS failures still cluster and get triaged along platform-specific lines). We go deeper on CI cost, OS fragmentation, and release-mechanism differences in Android vs iOS Testing: Key Differences in Frameworks, Tools & CI.
One dashboard for Android and iOS
Most mobile teams run Espresso and XCTest as two entirely separate pipelines with two separate result stores, because the platforms have nothing in common upstream — different languages, different build tools, different CI jobs. That split then propagates downstream: an Android-only view and an iOS-only view, no combined pass rate, no way to tell whether this week’s app-wide reliability trend is actually improving or just moving flakiness from one platform to the other.
Unifying Android and iOS results into one view is the same problem monorepo test aggregation solves for a repo running Playwright, Jest, and pytest side by side — different frameworks, different formats, one combined health picture per commit. Since Espresso, XCTest (once converted), and Maestro all resolve to JUnit-XML, they land in the same project and the same trend line once both platforms upload to it. We walk through the setup end-to-end in One Dashboard for Espresso, XCTest & Maestro, and cover what’s worth tracking once that dashboard exists in Mobile Test Reporting: What to Track Beyond Pass/Fail.
AI failure clustering on mobile results
Mobile suites make failure clustering earn its keep faster than most. A single broken bottom-sheet component, a changed permission prompt, or one flaky third-party SDK can fail dozens of Espresso or XCUITest cases at once — each looking like a distinct, unrelated failure in a flat list. AI failure clustering groups failures that share a root cause using signals like error message, stack trace, and failure timing, so triage on a red mobile build starts from a handful of causes instead of forty individual test names. This matters even more once Android and iOS results share one dashboard, since the same underlying regression — a broken API contract, a bad feature flag — can surface as differently-worded failures on each platform and still needs to be recognized as one problem.
Mobile CI/CD and release readiness
Mobile release cadence adds a constraint web teams don’t have: once a build ships, you can’t quietly roll it back the way you’d revert a bad web deploy. App Store and Play Store review adds days of lag, and a broken release sits in front of users until a follow-up build clears review. That asymmetry is exactly why release readiness — turning “is this safe to ship?” from a gut call into evidence — matters more on mobile than almost anywhere else in the stack.
A mobile quality gate built on results analysis, not just a raw pass rate, asks: are today’s failures real regressions or known-flaky tests, do they cluster around a critical path (checkout, auth, onboarding), and is the flake rate trending up or down relative to the last few builds. A CI pipeline that only checks “did the suite pass” can’t answer any of that — it treats a build with three flaky, previously-quarantined failures the same as a build with three brand-new regressions in checkout. Feeding Espresso, XCTest, and Maestro results into an observability layer after every CI run turns that gate from a single boolean into a build-over-build risk trend, which is the same underlying signal that improves DORA metrics like change failure rate on any platform.
A concrete version of this gate looks like: Android and iOS CI jobs each run their instrumented suites, upload results (JUnit-XML directly for Espresso and Maestro, converted JUnit-XML for XCTest), and a release-readiness check runs after both platforms report in. If Android shows a new failure cluster in the checkout flow and iOS is clean, the gate blocks the Android build for that release train while iOS proceeds — a distinction a single pass/fail check across “mobile” as one bucket would blur. If both platforms show only previously-quarantined flaky tests failing, the gate can pass with a note rather than block a release train over noise it already knows about. We cover pipeline design end-to-end in Mobile CI/CD Testing: A Pipeline That Catches Failures Before the App Store Does and the specific gate criteria in Mobile Release Readiness: Quality Gates for Android & iOS.
Mobile QA foundations: manual, automated, and the right split
Not everything on a mobile release checklist is worth automating, and pretending otherwise wastes effort in both directions. Automated Espresso/XCUITest/Maestro coverage earns its keep on regression-prone, high-traffic paths that run on every commit — login, checkout, core navigation — where a flaky-but-fixable test still beats no coverage at all. Manual testing earns its keep on the things automation is bad at: genuinely subjective UX judgment, one-off exploratory passes on a new OS beta, and physical-device checks (camera, biometrics, real push notifications, real cellular handoff) that are expensive or impossible to fully simulate even on a device cloud. Most healthy mobile QA processes run both, with automated results feeding the observability layer continuously and manual passes concentrated around release candidates and major OS upgrades — the release-readiness score from the automated side then tells you where to point that manual attention. We cover how to size that split in Manual vs. Automated Mobile Testing: How to Split Your QA Effort, and what belongs on the checklist itself in Mobile App Testing Checklist: What to Test Before Every Release.
Mobile test observability vs. device-execution clouds
It’s worth returning to the taxonomy table one more time, because this specific pairing is the one that generates the most support questions: “why doesn’t Qualflare run my tests on a real iPhone?” The short answer is that running tests on hardware and analyzing what those runs produced are different jobs with different vendors. BrowserStack, Sauce Labs, and Firebase Test Lab solve provisioning — they hand your CI a real or virtual device matrix to execute against, and they’re good at it. Qualflare, TestRail, Zephyr, and similar tools solve what happens to the results afterward: flaky scoring, clustering, trends, and a release verdict.
These aren’t competitors; they’re adjacent layers, and a well-instrumented pipeline runs device-cloud execution first, then feeds the resulting JUnit-XML into an observability layer second. We wrote a direct, capability-by-capability comparison for exactly this question in Qualflare vs. BrowserStack Test Observability, including where BrowserStack’s own reporting stops and where an observability layer picks up.
Choosing a mobile test management or observability tool
Once you’ve separated “what runs my tests” from “what analyzes my results,” shortlisting the second category gets much easier. The field includes general test-management tools that added mobile support (TestRail, Zephyr, Testmo), observability-native platforms (Qualflare, ReportPortal, Allure TestOps), and device-cloud vendors whose own dashboards do some result analysis alongside execution (Kobiton). Evaluate them the same way you’d evaluate any observability tool: on your own Espresso/XCTest/Maestro data, not a vendor demo, weighing setup friction (does it need a reporter plugin per framework, or does it read whatever JUnit-XML your CI already writes), flaky-detection quality, and whether release-risk scoring is a real feature or a rebadged pass-rate chart. The full comparison — pricing, integration depth, and where each tool’s mobile support actually stops — is in Best Mobile Test Management & Observability Tools (2026).
Getting started with Qualflare for mobile
The integration follows the same shape as every other framework Qualflare ingests: point the CLI at a results file, run one command, no reporter plugin to install. For Android, that’s the JUnit-XML Gradle already writes after connectedAndroidTest. For iOS, add one conversion step — run a7ex/xcresultparser against the .xcresult bundle xcodebuild test produces, then upload the resulting XML. For Maestro, add --format junit to your existing maestro test command and upload the output directly. All three land in the same project, on the same dashboard, scored by the same flaky-detection and failure-clustering engine that already covers your web and backend suites.
Start free with Qualflare — connect your Android and iOS CI jobs and see flaky detection, failure clustering, and release-risk scoring on your own mobile results within minutes.
Frequently asked questions
Does Qualflare run mobile tests on real devices?
No. Qualflare does not provision real devices, emulators, or simulators, and it does not execute your test suite. It ingests the results your existing device/CI setup already produces — from Espresso, XCTest/XCUITest, or Maestro — and analyzes them for flakiness, failure clusters, and release risk. Device provisioning and execution stay with your CI runner or a device-execution cloud like BrowserStack, Sauce Labs, or Firebase Test Lab.
What’s the difference between mobile test observability and a device cloud like BrowserStack?
A device cloud runs your tests: it provisions real or virtual Android and iOS devices so your suite executes across many OS versions, screen sizes, and hardware configurations. Mobile test observability starts after that run finishes — it analyzes the results the run produced, detecting flaky tests from historical pass/fail data, clustering failures by shared root cause, and scoring whether a release is safe to ship. They solve different, complementary problems; most mature mobile teams run both.
Which mobile frameworks does Qualflare support?
Qualflare ingests Android results from Espresso (native JUnit-XML via Gradle’s AndroidJUnitRunner), iOS results from XCTest/XCUITest (converted from .xcresult to JUnit-XML), and cross-platform UI results from Maestro (native JUnit-XML via maestro test --format junit). Appium suites are ingestible when they’re wrapped in a JUnit, TestNG, or pytest harness, since Appium itself has no official JUnit reporter. See the full list on the frameworks page.
What is a flaky mobile test?
A flaky mobile test passes and fails on the same app build without any code change — the same definition as a flaky test on any platform, but mobile suites hit it more often because they depend on more moving parts: device/emulator boot time, animation timing, network conditions, OS-level permission dialogs, and app-store or provisioning quirks that a web or unit test never encounters.
How do I get XCTest or XCUITest results into a format Qualflare can ingest?
Xcode writes test results as an .xcresult bundle, which is a binary format, not JUnit-XML. Convert it with a currently maintained tool — a7ex/xcresultparser is the actively developed option; the once-standard fastlane-community/trainer has had no release since 2019 and no commit since 2021, so it’s no longer a safe default. Run the converter in your CI step after xcodebuild test, then upload the resulting JUnit-XML file.
Does Qualflare support Appium or Detox/React Native tests?
Appium suites are ingestible when written with a JUnit, TestNG, or pytest harness, since those produce their own JUnit-XML output — Appium itself ships no official JUnit reporter. Detox (React Native) has no confirmed native ingestion path today; React Native and Flutter suites are reachable only indirectly, by driving the app through Maestro instead.


