Parallel Android UI Automation

At Halodoc, we're always striving to make our testing pipeline faster and more scalable. As our Android app continues to evolve with new features and deeper integrations, the need for robust and wide-reaching UI automation has grown just as rapidly. But with this growth, ensuring timely feedback without compromising on test quality became a challenge.

To tackle this, we introduced parallel test execution for our Android UI automation suite using Appium. This approach has helped us significantly reduce execution time, increase CI efficiency, and scale our test coverage across multiple devices—all without added infrastructure complexity.

In this blog post, we’ll walk you through how we implemented parallel Android execution, the challenges we faced, and the improvements it brought to our automation workflow.

The Challenge of Growing Test Suites

As mobile apps evolve, their codebases grow—and so do their test suites. A typical Android app can easily have hundreds of UI scenarios across multiple modules and user journeys.

Running these tests sequentially turns into a multi-hour task, slowing feedback loops and delaying releases.

At Halodoc, this became increasingly clear as we scaled our healthcare platform across Indonesia. Each new offering—teleconsultation, pharmacy delivery, lab diagnostics, digital clinics—added its own layer of test coverage.

With sequential execution, our test cycles grew longer, directly impacting our ability to release fast, high-quality updates.

Understanding Parallel UI Testing in Android

Running UI tests in parallel can greatly reduce execution time—but if multiple tests share the same emulator or Appium session, it can lead to conflicts and flaky results. To solve this, we built a fully isolated and scalable setup for Android.

At Halodoc, we use Appium as our mobile automation framework, combined with TestNG and Cucumber for test orchestration.

We run multiple Android emulators simultaneously, with each emulator connected to its own dedicated Appium server on a unique port. A custom TestNG XML suite assigns each test block to a specific emulator–Appium pair, ensuring isolation between tests.

TestNG is configured with parallel="tests", enabling concurrent execution of test classes or runners. To prevent interference between threads, we use ThreadLocal drivers, which ensure that each test thread manages its own session and device context.

This approach significantly reduced our overall execution time while maintaining the reliability and consistency needed for large-scale UI automation.

How We Implemented Parallel UI Testing for Android

  1. Multiple Emulator Instances: We launch several Android emulators in parallel—each tied to a unique Appium server running on a separate port. This setup ensures complete isolation, allowing tests to run independently without interference.
  2. Test Class Distribution: Our TestNG XML suite defines multiple test blocks—each mapped to a specific emulator–Appium server pair. Every runner is executed independently, enabling true parallelism at the suite level without modifying core test logic.
  3. Independent Execution Environments: Each emulator runs in a sandboxed environment with isolated app data, local storage, and session state. However, interference can still occur when tests share backend resources such as user accounts or APIs. To avoid this, we follow strict practices: using unique or dynamically generated test data per thread, stubbing or mocking external dependencies where possible, and ensuring that each test interacts with non-overlapping backend entities. These measures help minimize data collisions and significantly reduce test flakiness.
  4. Resource Optimization: The number of parallel emulators can be adjusted based on system resource availability (CPU, memory). We optimize performance by balancing emulator boot times, Appium startup latency, and the load distribution of test cases.
  5. Result Aggregation: Upon completion, each runner generates its own test report, which we aggregate into a unified summary using CI tools or custom scripts. This ensures complete visibility while keeping logs and metrics separated for easier debugging.

Parallel Testing with Appium + TestNG + Cucumber

At Halodoc, our Android UI test automation leverages:

  • Appium for device interactions
  • Cucumber for BDD using Gherkin syntax
  • TestNG for test orchestration and parallel execution

This combination empowers cross-functional collaboration and ensures our tests serve as both functional checks and living documentation.

Implementation Guide

  1. Emulator Cleanup & Appium Setup
    Before launching emulators, we ensure no stale emulator or Appium instances are running. This cleanup step is crucial to avoid port conflicts and device allocation errors.

Once cleaned, we start multiple Android emulators—each configured with a unique port. Every emulator is paired with its own Appium server instance, which is begun programmatically or via a Gradle task.

2. Define Feature Files and Dedicated Runners
To enable true parallel execution, we organize our test scenarios into distinct .feature files based on core user flows—such as login, consultation, and booking. These files reside under src/test/resources/features and define test steps using Gherkin syntax (Given, When, Then).

Each .feature file is associated with a dedicated runner class located in the runners package. These runners are annotated with @RunWith(Cucumber.class) and @CucumberOptions to specify the feature file path, step definitions, and reporting configurations.

This 1:1 mapping between feature files and runners forms the foundation of our parallel strategy. When integrated with TestNG, each runner can be independently assigned to a specific emulator–Appium pair. This ensures optimal thread utilization and significantly reduces overall execution time.

To maintain robustness, each scenario is designed to be self-contained, with no interdependencies or shared state. We use isolated or dynamically generated test data per thread, eliminating the risk of data collisions and flakiness.

This modular design allows us to avoid bottlenecks from monolithic test suites, reduce test flakiness, scale execution across multiple devices, and improve traceability. It also integrates seamlessly with our CI/CD pipelines, enabling effortless addition of new flows and contributing to a scalable, reliable, and parallel-ready automation framework.

3. Configure TestNG for Parallel Execution
We use a parameterised testng.xml file to run each runner on its own emulator–Appium combination. Here's a sample:

Each test block corresponds to a separate emulator and port combination.

4. Thread-Safe Driver Management
To ensure tests don’t interfere with each other, we use ThreadLocal<AndroidDriver> and ThreadLocal<AppiumDriverLocalService> to isolate both the driver and Appium service per test thread.

This approach ensures that every test thread uses its own Appium server and driver instance - eliminating shared state, avoiding port collisions, and enabling full test isolation.

5. Hooks & App Reset
We leverage Cucumber @Before hooks to reset the app before each scenario. This guarantees test isolation and consistency regardless of the execution order or environment.

6. CI Integration
The entire flow is triggered via Gradle commands in our CI/CD pipeline. Emulator boot, Appium start, and TestNG execution are orchestrated in sequence. Logs and reports from each thread are stored separately and aggregated post-execution.

Results: Real-world Performance Gains

After implementing parallel UI testing, we experienced transformative improvements in test execution time and overall efficiency. One of the most compelling outcomes came from a particularly large and complex Android UI testing suite:

  • Execution time dropped from ~2 hours to just ~40 minutes — a remarkable ~67% reduction.

This improvement wasn't an isolated case. Across the board, our test suites now run significantly faster, empowering us to:

  • Accelerate CI/CD pipelines
  • Shorten feedback loops for developers
  • Increase test frequency without resource bottlenecks
  • Catch regressions earlier with higher confidence

Parallel testing has shifted UI automation from a bottleneck into a strategic advantage, enabling rapid, reliable releases without compromising on quality.

Key Challenges and Our Solutions

While implementing parallel UI testing for Android, we encountered several technical challenges that impacted stability and scalability. Here’s how we tackled them:

  1. Driver/Session Collisions
    Challenge: When multiple threads attempted to access a shared Appium driver, it led to test instability and session overwrites.
    Solution: We resolved this by using ThreadLocal to instantiate a separate Appium driver for each thread, ensuring complete session isolation and avoiding collisions.
  2. Incorrect Feature Mapping with Runners
    Challenge: All feature files were executed across all available emulators, resulting in redundant runs and longer test times.
    Solution: We mapped specific runners to feature files using tagging and custom TestNG configurations. This ensured that each emulator handled only its assigned tests.
  3. Port Conflicts and Pre-occupied Appium Sessions
    Challenge: Conflicts arose due to hardcoded Appium server ports and already running sessions.
    Solution: We implemented dynamic port and device UDID allocation along with programmatic Appium server initialization for each emulator, eliminating port clashes.

By addressing these core challenges, we were able to build a stable, scalable, and CI-ready parallel execution setup for Android UI automation.

Conclusion

Parallel UI testing has significantly improved our development workflow at Halodoc. By cutting down test execution time by ~60%, we eliminated one of the major delays in our release cycle, without sacrificing coverage or quality.

This achievement wasn’t about toggling a feature; it required careful design, ensuring tests were independent, scenarios were evenly distributed, and environments were isolated.

The key practices that helped us scale—test independence, balanced runner distribution, and infrastructure readiness—can be applied to any mobile testing framework, not just Android or Appium.

As Halodoc continues to grow across Indonesia, fast and reliable test feedback helps us release new features confidently and deliver seamless healthcare experiences to millions of users.

References

Here are some helpful resources and tools we used while implementing parallel test execution on Android:

These references offer practical implementation techniques and foundational knowledge that helped us design a reliable and scalable parallel automation framework.

Join us

Scalability, reliability, and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels, and if solving hard problems with challenging requirements is your forte, please reach out to us with your resume at careers.india@halodoc.com.

About Halodoc

Halodoc is the number one all-around healthcare application in Indonesia. Our mission is to simplify and deliver quality healthcare across Indonesia, from Sabang to Merauke.
Since 2016, Halodoc has been improving health literacy in Indonesia by providing user-friendly healthcare communication, education, and information (KIE). In parallel, our ecosystem has expanded to offer a range of services that facilitate convenient access to healthcare, starting with Homecare by Halodoc as a preventive care feature that allows users to conduct health tests privately and securely from the comfort of their homes; My Insurance, which will enable users to access the benefits of cashless outpatient services more seamlessly; Chat with Doctor, which allows users to consult with over 20,000 licensed physicians via chat, video or voice call; and Health Store features that allow users to purchase medicines, supplements and various health products from our network of over 4,900 trusted partner pharmacies. To deliver holistic health solutions in a fully digital way, Halodoc offers Digital Clinic services, including Haloskin, a trusted dermatology care platform guided by experienced dermatologists.
We are proud to be trusted by global and regional investors, including the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. With over USD 100 million raised to date, including our recent Series D, our team is committed to building the best personalized healthcare solutions, and we remain steadfast in our journey to simplify healthcare for all Indonesians.