Skip to content

滑动交互方法

滑动交互方法

简介

在移动端应用中,基于简便的原因,用户通常会倾向于使用滑动操作来达到与应用程序中的控件进行交互的,这使得滑动成为自动化测试中频繁使用的关键动作。在 Appium 中提供了多种方式来实现模拟用户的滑动屏幕动作。

滑动操作的场景

移动端应用中的滑动场景,大致有如下几种类型:

  1. 触摸事件模拟:滑动操作的基本原理是模拟用户触摸屏幕的行为。通过发送一系列的触摸事件,模拟用户按住、滑动和释放的动作,可以实现在屏幕上滑动的效果。

  2. 坐标计算:滑动操作通常涉及到起始点和终点的坐标计算,起始点表示滑动的起始位置,终点表示滑动的结束位置。这些坐标可以是相对于屏幕或特定元素的坐标。

  3. 惯性滑动:有些滑动操作在用户释放手指后还会继续滑动一段距离,模拟惯性滑动的效果,这通常需要在滑动过程中模拟逐渐减速的过程。

滑动的方式

swipe 方法

从一个点滑动到另一个点,可选择持续时间,具有滑动的惯性。需要的参数如下:

  • start_x:开始坐标 x。
  • start_y:开始坐标 y。
  • end_x:结束坐标 x。
  • end_y:结束坐标 y。
  • duration(可选):滑动持续的时间,默认为 0。

使用 appium 官方 Demo apk 进行练习,apk 网盘地址

Python 代码示例

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy

class TestSwipe:

    def setup_class(self):
        '''
        完成 capability 设置
        初始化 driver
        :return:
        '''
        # 设置 cpability
        caps = {
            # 设置 app 安装的平台(Android,iOS)
            "platformName": "Android",
            # 设置 appium 驱动
            "appium:automationName": "uiautomator2",
            # 设置设备名称
            "appium:deviceName": "emulator-5554",
            # 设置被测 app 的包名
            "appium:appPackage": "io.appium.android.apis",
            # 设置被测 app 启动页面的 Activity
            "appium:appActivity": ".ApiDemos"
        }

        # 初始化 driver
        self.driver = webdriver.Remote(
            "http://127.0.0.1:4723",
            options=UiAutomator2Options().load_capabilities(caps)
        )
        # 设置全局隐式等待
        self.driver.implicitly_wait(5)

    def teardown_class(self):
        '''
        关闭 driver
        :return:
        '''
        self.driver.quit()

    def test_swipe_views(self):
        # 从一个点滑动到另一个点,可选择持续时间
        '''
        start_x: 开始坐标 x
        start_y: 开始坐标 y
        end_x: 结束坐标 x
        end_y: 结束坐标 y
        duration: 持续时间
        '''
        # 点击进入 Views 界面
        self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, 'Views').click()
        # 获取窗口的宽和高
        size = self.driver.get_window_size()
        height = size.get('height')
        weight = size.get('width')
        # 执行滑动操作
        self.driver.swipe(
            start_x=weight * 0.5,
            start_y=height * 0.8,
            end_x=weight * 0.5,
            end_y=height * 0.2,
            duration=2000
        )
        # 查找 Picker 元素
        ele = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, 'Picker')
        # 断言滑动成功 找到元素
        assert ele.text == 'Picker'

Java 代码示例

// Java 中没有提供对应的便捷方法,需要使用自定义的方法来实现对应操作
package org.example;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.remote.MobileCapabilityType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestSwipe {

    private static AndroidDriver driver;

    @BeforeAll
    public static void setUpClass() throws MalformedURLException {
        // 初始化capability
        DesiredCapabilities caps = new DesiredCapabilities();
        // 设置 app 安装的平台(Android、iOS)
        caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        // 设置 appium 驱动
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        // 设置设备名称
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, "emulator-5554");
        // 设置 app 的包名
        caps.setCapability("appPackage", "io.appium.android.apis");
        // 设置 app 的启动页
        caps.setCapability("appActivity", ".ApiDemos");
        // 设置 app 不清空缓存
        caps.setCapability("appium:noReset", true);
        // 设置 app 不重启
        caps.setCapability("appium:shouldTerminateApp", true);

        // 设置启动url
        URL remoteUrl = new URL("http://127.0.0.1:4723");
        // 初始化driver
        driver = new AndroidDriver(remoteUrl, caps);
    }

    @AfterAll
    public static void teardownClass() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    public void testSwipeViews() {
        // 点击进入 Views 界面
        driver.findElement(AppiumBy.accessibilityId("Views")).click();

        // 获取窗口的宽和高
        Dimension size = driver.manage().window().getSize();
        int height = size.getHeight();
        int width = size.getWidth();

        // 执行滑动操作
        int startX = (int) (width * 0.5);
        int startY = (int) (height * 0.8);
        int endX = (int) (width * 0.5);
        int endY = (int) (height * 0.2);

        swipe(startX, startY, endX, endY, Duration.ofSeconds(2));

        // 查找 Picker 元素
        WebElement ele = driver.findElement(AppiumBy.accessibilityId("Picker"));

        // 断言滑动成功 找到元素
        assertEquals("Picker", ele.getText());
    }

    public void swipe(int startX, int startY, int endX, int endY, Duration duration) {
        PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
        Sequence swipe = new Sequence(finger, 1)
                .addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), startX, startY))
                .addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
                .addAction(finger.createPointerMove(duration, PointerInput.Origin.viewport(), endX, endY))
                .addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
        driver.perform(Collections.singletonList(swipe));
    }
}

scroll 方法

从一个元素滑动到另一个元素,具有滑动的惯性。需要的参数如下:

  • origin_el:滑动的起始元素。
  • destination_el:滑动的结束元素。

使用 appium 官方 Demo apk 进行练习,apk 网盘地址

Python 代码示例

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy

class TestSwipe:

    def setup_class(self):
        '''
        完成 capability 设置
        初始化 driver
        :return:
        '''
        # 设置 cpability
        caps = {
            # 设置 app 安装的平台(Android,iOS)
            "platformName": "Android",
            # 设置 appium 驱动
            "appium:automationName": "uiautomator2",
            # 设置设备名称
            "appium:deviceName": "emulator-5554",
            # 设置被测 app 的包名
            "appium:appPackage": "io.appium.android.apis",
            # 设置被测 app 启动页面的 Activity
            "appium:appActivity": ".ApiDemos"
        }

        # 初始化 driver
        self.driver = webdriver.Remote(
            "http://127.0.0.1:4723",
            options=UiAutomator2Options().load_capabilities(caps)
        )
        # 设置全局隐式等待
        self.driver.implicitly_wait(5)

    def teardown_class(self):
        '''
        关闭 driver
        :return:
        '''
        self.driver.quit()

    def test_scroll_ele(self):
        '''
        从一个元素滑动到另一个元素
        '''
        # 点击进入 Views 界面
        self.driver.find_element(
            AppiumBy.ACCESSIBILITY_ID, 'Views'
        ).click()
        # 滑动起始元素
        image_btn = self.driver.find_element(
            AppiumBy.ACCESSIBILITY_ID, 'ImageButton'
        )
        # 滑动结束元素
        button = self.driver.find_element(
            AppiumBy.ACCESSIBILITY_ID, 'Buttons'
        )
        # 执行滑动操作
        self.driver.scroll(image_btn, button, duration=2000)
        # 查找 Picker 元素
        ele = self.driver.find_element(
            AppiumBy.ACCESSIBILITY_ID, 'Picker'
        )
        assert ele.text == 'Picker'

Java 代码示例

// Java 中没有提供对应的便捷方法,需要使用自定义的方法来实现对应操作
package org.example;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.MobileCapabilityType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestSwipe {

    private static AndroidDriver driver;

    @BeforeAll
    public static void setUpClass() throws MalformedURLException {
        // 初始化capability
        DesiredCapabilities caps = new DesiredCapabilities();
        // 设置 app 安装的平台(Android、iOS)
        caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        // 设置 appium 驱动
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        // 设置设备名称
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, "emulator-5554");
        // 设置 app 的包名
        caps.setCapability("appPackage", "io.appium.android.apis");
        // 设置 app 的启动页
        caps.setCapability("appActivity", ".ApiDemos");

        // 设置启动url
        URL remoteUrl = new URL("http://127.0.0.1:4723");
        // 初始化driver
        driver = new AndroidDriver(remoteUrl, caps);
        // 设置全局隐式等待时间
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
    }

    @AfterAll
    public static void teardownClass() {
        if (driver != null) {
            // 关闭 driver
            driver.quit();
        }
    }

    @Test
    public void testScrollEle() {
        // 点击进入 Views 界面
        driver.findElement(AppiumBy.accessibilityId("Views")).click();
        // 滑动起始元素
        WebElement imageBtn = driver.findElement(AppiumBy.accessibilityId("ImageButton"));
        // 滑动结束元素
        WebElement button = driver.findElement(AppiumBy.accessibilityId("Buttons"));
        // 执行滑动操作
        scroll(imageBtn, button, Duration.ofSeconds(2));
        // 查找 Picker 元素
        WebElement ele = driver.findElement(AppiumBy.accessibilityId("Picker"));
        // 断言滑动成功 找到元素
        assertEquals("Picker", ele.getText());
    }

    public void scroll(WebElement startElement, WebElement endElement, Duration duration) {
        int startX = startElement.getLocation().getX() + startElement.getSize().getWidth() / 2;
        int startY = startElement.getLocation().getY() + startElement.getSize().getHeight() / 2;
        int endX = endElement.getLocation().getX() + endElement.getSize().getWidth() / 2;
        int endY = endElement.getLocation().getY() + endElement.getSize().getHeight() / 2;

        PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
        Sequence swipe = new Sequence(finger, 1)
                .addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), startX, startY))
                .addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
                .addAction(finger.createPointerMove(duration, PointerInput.Origin.viewport(), endX, endY))
                .addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));

        driver.perform(Collections.singletonList(swipe));
    }
}

总结

  • 滑动操作的场景
  • 使用 swipe 方法滑动
  • 使用 scroll 方法滑动