Automation testing for iOS apps with Appium

4 min read
zen8labs automation testing for iOS apps with Appium

Introduction

The mobile development world is one of the most competitive landscapes out there, any good company such as zen8labs have to go to great lengths to ensure a good user experience across multiple iOS devices is crucial. Automation testing with Appium helps teams deliver high-quality apps by automating UI tests, reducing manual effort, and improving test reliability. 

In this guide, I’m going to show you how I set up and run automation tests for iOS apps using Appium, WebdriverIO, and Bitrise CI/CD. I’ll share some real examples, handy tips, and the best practices that have worked well for me, plus a few debugging tricks.

Why choose Appium for iOS automation?

Appium is a powerful, open-source mobile automation framework that supports iOS and Android apps. It offers: 

  • Cross-platform support (Android & iOS) 
  • No need to modify the app (supports native, web, and hybrid apps) 
  • Integration with WebdriverIO for JavaScript-based tests 
  • Works with real devices & simulators

Use case: If you’re developing an iOS app with Flutter, Swift, or React Native, Appium allows you to test it on different iOS devices with a single test suite.

Comparing Appium with other mobile automation tools

When choosing an automation tool for mobile testing, it’s essential to compare Appium with other leading frameworks like XCUITest and Espresso.

FeatureAppiumXCUITest (ios only)Espresso (android only)
Cross-platformYes (iOS & Android) No (iOS only)  No (Android only) 
Requires app modificationNoYes (requires code instrumentation)Yes (requires embedding into the app)  
Supports native & web appsYesNo (native only)No (native only)
Parallel executionYesYesYes
CI/CD integrationYesYesYes
PerformanceSlower compared to native toolsFastFast
Ease of setupRequires Appium server & WebDriverAgent Integrated in Xcode Integrated in Android Studio 

The positives and negatives of the tools

Appium pros:

  • Works for both iOS & Android, making it an excellent choice for cross-platform teams. 
  • No need to modify the app’s source code. 
  • Supports multiple programming languages (JavaScript, Java, Python, etc.). 
  • Open source with a large community. 

Appium cons:

  • Slower execution speed compared to XCUITest and Espresso due to its WebDriver-based architecture. 
  • Requires setting up Appium server and WebDriverAgent, which can be complex. 
  • Less stable on iOS, as updates in iOS can sometimes break compatibility. 

XCUITest pros (for iOS):

  • Faster execution since it’s natively integrated into Xcode. 
  • More stable for iOS apps as it’s maintained by Apple. 
  • No need for an external Appium server. 

XCUITest cons:

  • Only supports iOS (no Android support). 
  • Requires modifying the app by embedding test code. 

Espresso pros (for Android): 

  • Fast execution as it runs directly within the app. 
  • Reliable and stable for Android UI testing. 
  • No need for an external server. 

Espresso cons: 

  • Only supports Android (no iOS support). 
  • Requires embedding the Espresso testing framework into the app’s code. 

Which one should you choose? 

  • If your team is building a cross-platform app (iOS & Android) → Appium is the best choice. 
  • If you are testing iOS-only apps → XCUITest is faster and more stable. 
  • If you are testing Android-only apps → Espresso is the best choice for speed and stability. 

Pro tip: Many teams use Appium for cross-platform UI tests and combine it with XCUITest/Espresso for platform-specific optimizations.

Step 1: Setting up Appium for iOS

Before writing automation tests, you need to set up Appium and WebdriverIO on your local machine or CI/CD environment.

1.1 Install required dependencies

#Install Node.js & WebdriverIO 

 
brew install node 
 
npm install -g webdriverio appium 

#Install Appium drivers for iOS 

 
appium driver install xcuitest 

#Ensure Xcode and WebDriverAgent are configured 

 
xcode-select --install 

Pro tip: If WebDriverAgent fails to launch, manually configure signing in Xcode for WebDriverAgentRunner.

Step 2: Writing your first Appium test

Now that our setup is ready, let’s write our first test using WebdriverIO.

2.1 Project structure

my-ios-tests/ 
├── tests/ 
│   ├── login-test.js  # Example test file 
├── wdio.conf.js       # WebdriverIO configuration 
├── package.json       # Dependencies 

2.2 WebdriverIO configuration (wdio.conf.js)

exports.config = { 
    runner: 'local', 
    specs: ['./tests/*.js'], 
    capabilities: [{ 
        platformName: 'iOS', 
        'appium:deviceName': 'iPhone 14', 
        'appium:platformVersion': '17.7', 
        'appium:automationName': 'XCUITest', 
        'appium:app': '/path/to/your/app.app' 
    }], 
    framework: 'mocha', 
    reporters: ['spec'], 
};

2.3 Writing a simple test (login-test.js)

describe('Login test', () => { 
    it('should log in with valid credentials', async () => { 
        const emailField = await $('~emailTextField'); 
        await emailField.setValue('test@example.com'); 
 
        const passwordField = await $('~passwordTextField'); 
        await passwordField.setValue('SuperSecret123'); 
 
        const loginButton = await $('~loginButton'); 
        await loginButton.click(); 
    }); 
});

Pro tip: Use ~ for accessibility IDs instead of XPath for better performance.

Step 3: Running tests on Bitrise CI/CD

3.1 Add WebdriverIO Tests to Bitrise Workflow

Modify bitrise.yml to install dependencies, start Appium, and run tests:

workflows: 
  primary: 
    steps: 
      - script: 
          title: Install Appium & WebdriverIO 
          inputs: 
            content: | 
              npm install -g appium webdriverio 
      - script: 
          title: Run Appium Tests 
          inputs: 
            content: | 
              npx wdio run wdio.conf.js 

Pro Tip: Use BITRISE_XCODE_TEST_DEVICE to dynamically select the test device in CI/CD.

Step 4: Enhancing reports with Allure

To generate these detailed test reports, then you must integrate Allure:

4.1 Install Allure Reporter

npm install @wdio/allure-reporter --save-dev 

4.2 Update wdio.conf.js to capture screenshots on failures

const allureReporter = require('@wdio/allure-reporter'); 
 
exports.config = { 
    reporters: [['allure', { outputDir: './allure-results' }]], 
    afterTest: async function (test, context, { error, passed }) { 
        if (!passed) { 
            const screenshot = await driver.takeScreenshot(); 
            await allureReporter.addAttachment('Screenshot', Buffer.from(screenshot, 'base64'), 'image/png'); 
        } 
    } 
}; 

4.3 Generate Allure report

npx allure generate ./allure-results --clean 
npx allure open 

Pro Tip: Store the Allure reports in Bitrise Artifacts for easy access in CI/CD.

Final thoughts

Appium is a powerful automation tool for iOS apps, and when combined with WebdriverIO and Bitrise, it creates a robust mobile testing pipeline. By following this guide, you’ll: 

– Set up Appium and WebdriverIO for iOS automation
– Run tests locally and on CI/CD
– Capture screenshots and generate reports
– Handle scrolling, alerts, and more

By doing all of this, you’ll not only have a powerful automation tool for iOS but also the skills to allow you to build upon your knowledge. If you want to continue your learning journey to allow yourself the chance to build something awesome then check out these insights, for all things IT.

Hiep Nguyen, Project Manager

Related posts

Agile development has transformed the software industry by emphasizing and creating a space of collaboration. Learn about an aspect of Agile in our new blog
4 min read

In software development, the sooner you test, the faster and more efficiently you can deliver a high-quality product. At zen8labs, we take this philosophy to heart, making early testing a foundational part of our process. By testing as soon as possible – whenever there is any project document to review or work on – we

7 min read
Waits commands in Selenium are essential for creating efficient test automation. Discover Selenium's implicit, explicit, and fluent wait command with this guide.
7 min read