Mobile App Automation Testing with Appium: Advanced Techniques

Test Device Configuration

Use ADB to launch specific app pages:

adb shell start -n [package_name]/[activity_name]

Creating a new session starts a fresh testing environment.

Download applications to your computer using official app stores like Baidu App Store.

Install applications using:

adb install -r app_name.apk

Alternatively, drag and drop the APK file directly into the emulator.

To install applications on multiple emulators or devices simultaneously:

Uninstall applications with:

adb uninstall [package_name]

Application File Management

Installation Methods

  1. Download from official app stores (Google Play, App Store)
  2. Download from web platforms (like Baidu App Store) and transfer to device
  3. Install via command line:
adb install app_name.apk

Uninstallation Methods

  1. Uninstall directly from the emulator by long-pressing the app and dragging to delete
  2. Use command line:
adb uninstall package_name

Example for WeChat Work:

adb uninstall com.tencent.wework

Appium Desktop Setup

Click "Start Server" to initiate the Appium service.

Configure Desired Capabilities as follows:

{
  "platformName": "android",
  "deviceName": "emulator-5554",
  "appPackage": "com.tencent.wework",
  "appActivity": ".launch.LaunchSplashActivity",
  "autoGrantPermissions": "true",
  "noReset": "true"
}

Essential ADB Commands

  • adb devices - List connected devices
  • adb logcat | grep -i Displayed - Filter app activity transitions
  • adb shell am start -n [package]/[activity] - Launch specific activity

Example activities for WeChat Work:

com.tencent.wework/.launch.LaunchSplashActivity
com.tencent.wework/.launch.WwMainActivity

Note: The splash screen and main activity are different. While theoretically any page can be directly accessed, developers often restrict this to simulate real user scenarios.

Port Analysis in Mobile Testing

self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

0.0.0.0 is equivalent to localhost and should match the port in your script. Port 4723 is used for establishing communication between server and client.

UIAutomator2 uses a server APK on the device that typically operates on port 8200 to receive requests from the server.

Port 5037 is used for ADB communication between Android devices and PC (Android Debug Bridge).

Ports in the 8000+ range are typiclaly used for Chromedriver communication with webviews.

Element Positioning Strategies

Appium Desktop allows saving previous configurations for reuse.

When multiple elements share the same identifier, use sibling nodes or other attributes to differentiate. Both By.XPATH and MobileBy can be employed for ID or xpath searches.

For list pages requiring scrolling (like adding contacts in WeChat Work with many entries), use scrollable views:

'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("添加成员").instance(0))'

Element positioning principles:

  • Always use IDs when available
  • Avoid excessive explicit waits (though 1-2 seconds can be helpful during debugging)

For example, to find a name input field, searching by name is more reliable than by placeholder text. Use contains() for partial text matches:

"//*[contains(@text,'姓名')]/..//*[@resource-id='com.tencent.wework:id/ase']"

The double dots (..) represent the parent node, allowing you to find a child with a specific ID.

The same approach can be used for other elements like gander selection.

For toast notifications (brief system messages), locate them by class attribute or XPath:

toast = self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text
assert '添加成功' in toast

When multiple elements share the same text attribute, the first matching element is located by default.

Current Activity Identification

Method 1: Use Appium Desktop to identify the current activity.

Method 2: Programmatically get the current activity and assert it matches expectations.

Debugging Approaches

Identify error locations by line numbers and add print statements. Compare expected and actual values to pinpoint discrepancies. If issues persist, examine server-side error messages.

Clicking on error lines in your IDE can directly navigate to the problematic code.

Test Case Parameterization

Parameterization normally restarts the app for each parameter set. To avoid this, use class methods with teardown methods that return to the previous screen.


from time import sleep
from appium import webdriver
import yaml
import pytest
from appium.webdriver.common.mobileby import MobileBy

class TestContactAdd():
    def setup_class(self):
        desired_caps = {}
        desired_caps['platformName'] = 'android'
        desired_caps['platformVersion'] = '6.0'
        desired_caps['deviceName'] = 'emulator-5554'
        desired_caps['appPackage'] = 'com.tencent.wework'
        desired_caps['appActivity'] = '.launch.LaunchSplashActivity'
        desired_caps['noReset'] = 'true'
        desired_caps['dontStopAppOnReset'] = 'true'
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
        self.driver.implicitly_wait(5)

    def setup(self):
        pass

    def teardown(self):
        self.driver.find_element(MobileBy.ID, 'com.tencent.wework:id/gpp').click()

    def teardown_class(self):
        self.driver.quit()

    @pytest.mark.parametrize("mobile,name,gender", [('17801153312', 'TestUser12', 'Male'), ('17801153311', 'TestUser11', 'Female')])
    def test_add_member(self, mobile, name, gender):
        # Navigate to contacts
        contacts = self.driver.find_element_by_xpath("//*[@text='Contacts']")
        contacts.click()
        
        # Scroll to and click "Add Member"
        add_member = self.driver.find_element_by_android_uiautomator(
            'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("Add Member").instance(0))')
        add_member.click()
        
        # Click manual add button
        self.driver.find_element(MobileBy.ID, 'com.tencent.wework:id/c56').click()
        sleep(2)
        
        # Enter name
        name_field = self.driver.find_element(MobileBy.XPATH,
                                 "//*[contains(@text,'Name')]/..//*[@resource-id='com.tencent.wework:id/ase']")
        name_field.send_keys(name)
        
        # Enter mobile number
        mobile_field = self.driver.find_element(MobileBy.ID, 'com.tencent.wework:id/emh')
        mobile_field.send_keys(mobile)
        
        # Select gender
        gender_button = self.driver.find_element(MobileBy.ID, 'com.tencent.wework:id/ate')
        gender_button.click()
        
        # Choose specific gender
        self.driver.find_element(MobileBy.XPATH, f"//*[@text='{gender}']").click()
        
        # Save the new contact
        save_button = self.driver.find_element(MobileBy.ID, 'com.tencent.wework:id/gq7')
        save_button.click()
        
        sleep(2)
        
        # Verify success toast
        toast = self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text
        assert '添加成功' in toast

Alternatively, use fixtures to avoid code redundancy:


@pytest.fixture
def app_setup():
    desired_caps = {
        'platformName': 'android',
        'platformVersion': '6.0',
        'deviceName': 'emulator-5554',
        'appPackage': 'com.tencent.wework',
        'appActivity': '.launch.LaunchSplashActivity',
        'noReset': 'true',
        'dontStopAppOnReset': 'true'
    }
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    driver.implicitly_wait(5)
    yield driver
    driver.quit()

Tags: appium Android Testing Mobile Automation ADB UIAutomator2

Posted on Thu, 09 Jul 2026 17:31:13 +0000 by jikishlove