移动端自动测试工具 Appium 快速入门

Posted zuozewei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端自动测试工具 Appium 快速入门相关的知识,希望对你有一定的参考价值。

一、前言

今天简单介绍 移动端 UI 自动测试工具 Appium。

二、Appium 概述

Appium 是一个自动化测试开源工具,支持 ios 平台和 android 平台上的原生应用,web 应用和混合应用。“移动原生应用”是指那些用 iOS SDK 或者 Android SDK 写的应用。所谓的“移动web 应用”是指使用移动浏览器访问的应用(Appium 支持 iOS 上的 Safari 和 Android 上的 Chrome)。所谓的“混合应用”是指原生代码封装网页视图——原生代码和 web 内容交互。Appium 既能在 window 安装也能在 Mac 上安装,但是 window 上只能跑安卓设备,Mac 上能跑安卓与 IOS 两个设备。

Guihub:You can write tests with your favorite dev tools using any WebDriver-compatible language such as Java, Objective-C, javascript (Node), php, Python, Ruby, C#, Clojure, or Perl with the Selenium WebDriver API and language-specific client libraries.

源码地址:https://github.com/appium/appium

1、架构图

2、UI 自动化收益

任何 UI 自动化测试都不能完部替代人工测试,收益率高不高看部门怎么使用任何工具使用都是两方看怎么使用,如果有重复的工作每次需要人工去回归,建议使用自动化去回归,部门大家都用自动使用,会让大家的心信提高因为每次都机会使用自己写的脚本去验证自己重复工作。

脚本维护成本真的高吗?大家都说成本高,自己是否真的维护过,写过脚本?如果没有写过,没有维护过,没有发言权。只有自己用了才知道是否高。

三、环境安装

1、桌面版本安装

打开下面链接选择版本为exe进行下载:

下载安装后:

点击启动:

2、DOS命令安装


安装JDK

下载地址:https://www.oracle.com/technetwork/java/javase/downloads/index.html

配置环境变量:

JAVA_HOME:

JAVA_HOME=C:\\Program Files (x86)\\Java\\jdk1.8.0_181
%JAVA_HOME%\\bin;%JAVA_HOME%\\jre\\bin;
CLASSPATH:
.;%JAVA_HOME%\\lib;%JAVA_HOME%\\lib\\tools.jar

Java 验证:

java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) Client VM (build 25.181-b13, mixed mode, sharing)

3、安装SDK

下载地址:

配置环境变量:

ANDROID_HOME
C:\\Program Files (x86)\\android-sdk-windows
Path:
;%ANDROID_HOME%\\tools;%ANDROID_HOME%\\platform-tools
## 下载 node
http://nodejs.cn/download/
## 安装 appium
npm install -g appium

## 如果上面下载比较慢可以使用如下命名
## cnpm 安装
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install -g appium --no-cache
cnpm i appium-doctor
appium -v

安装验证环境命令:appium-doctor

执行命令验证是否成功:

Appium 版本检查与运行显示:

注意:如果上面环境没有配置,请自己搜索解决。

四、常用操作方法

  • 点击:click()
  • 输入:
sendKeys(CharSequence... keysToSend);
  • 清除:clear()
  • 长按:
     /**
     * 购物车商品图片
     * 长按
     * @param driver
     */
     
    public void cartSingleProductImage(AndroidDriver<AndroidElement> driver, String coordinate) {
        WaitUtil.waitWebElement(driver, getByLocator.getLocatorApp(coordinate), "长按购物车商品图片-弹出收藏与删除浮层");
        element = driver.findElement(getByLocator.getLocatorApp(coordinate));

        int x = element.getLocation().getX();
        int y = element.getLocation().getY();
        TouchAction action = new TouchAction(driver);
        //长按
        action.longPress(PointOption.point(x, y)).release().perform();}
  • 滑动:
       WebElement webElement = null;
        try {
            driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
            webElement = driver.findElementByAndroidUIAutomator(
                    "new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text(\\"See more details\\"))");
        } catch (Exception e) {
            e.printStackTrace();
        }
  • 上下左右滑动,最简单的下滑命令行:(uuid 表示手机设备号)
adb -s " + uuid + " shell input touchscreen swipe 400 800 400 400
     /**
         * 滑动方法
         *
         * @param driver
         * @param direction up、down、left、right
         */

        static Duration duration = Duration.ofSeconds(1);
        public static void swipe(AndroidDriver<AndroidElement> driver, String direction) {
            switch (direction.toLowerCase()) {
                case "up":
                    SwipeUp(driver);
                    break;
                case "down":
                    SwipeDown(driver);
                    break;
                case "left":
                    SwipeLeft(driver);
                    break;
                case "right":
                    SwipeRight(driver);
                    break;
                default:
                    System.out.println("方向参数不对,只能是up、down、left、right");
                    break;
            }
        }


        /**
         * 上滑
         *
         * @param driver
         */

        public static void SwipeUp(AndroidDriver<AndroidElement> driver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Dimension size = driver.manage().window().getSize();
            int height = size.height;
            int width = size.width;
            new TouchAction(driver).longPress(PointOption.point(width / 2, 100))
                    .moveTo(PointOption.point(width / 2, height - 100)).release()
                    .perform();

        }


        /**
         * 下滑
         *
         * @param driver
         */

        public static void SwipeDown(AndroidDriver<AndroidElement> driver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Dimension size = driver.manage().window().getSize();
            int height = size.height;
            int width = size.width;
            new TouchAction(driver)
                    .longPress(PointOption.point(width / 2, height - 100))
                    .moveTo(PointOption.point(width / 2, 100)).release().perform();
        }


        /**
         * 左滑
         *
         * @param driver
         */

        public static void SwipeLeft(AndroidDriver<AndroidElement> driver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Dimension size = driver.manage().window().getSize();
            int height = size.height;
            int width = size.width;
            new TouchAction(driver)
                    .longPress(PointOption.point(width - 100, height / 2))
                    .moveTo(PointOption.point(100, height / 2)).release().perform();
        }


        /**
         * 右滑
         *
         * @param driver
         */

        public static void SwipeRight(AndroidDriver<AndroidElement> driver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Dimension size = driver.manage().window().getSize();
            int height = size.height;
            int width = size.width;
            new TouchAction(driver).longPress(PointOption.point(100, height / 2))
                    .moveTo(PointOption.point(width - 100, height / 2)).release()
                    .perform();
        }
  • 获取属性:getAttribute()
  • 获取文本:getText()
  • 获取资源:getPageSource()
  • 元素定位,两种方式。
    • 第一种使用 sdk 中的【uiautomatorviewer.bat】进行元素定位
      打开:

双击 uiautomatorviewer.bat 即可弹出:

在操作上面之前需要链接手机或者链接模拟器并操作命令显示:adb devices
如果是模拟器需要先链接:adb connect 127.0.0.1:62001 这样再次链接.

模拟器链接显示:

点击 sdk 中的【uiautomatorviewer.bat】

链接成功显示:

鼠标点击某个控件就会提示该控件可操作的相应内容:

说明:

其实在做移动端自动化测试,定位方式很少基本就是 id/name/xpath/ 坐标等定位方式。

五、常见定位方式

1、ID 定位

 driver.findElement(By.id("xxxxxx")).click();

2、name定位

 driver.findElement(By.name("xxxxxx")).click();

3、xpath 定位

xpath定位是最常用的一种方式,可以去学习下 xpath 语法:

但是网上也有大牛做一个插件,做 ui 自动化可直接使用:- https://github.com/lazytestteam/lazyuiautomatorviewer

大家下载后替换 sdk 中的 uiautomatorviewer.jar 就可使用。

点击 uiautomatorviewer.bat 再次弹出如下:

driver.findElement(By.xpath("xxxxxx")).click();

4、定位 h5 页面

启动:

点击:

再弹出对话中输入:

在下面选项框中输入:
需要获取 appPackage 与 appActivity

使用命令:

aapt d badging pinduoduov4.76.0_downcc.com.apk |findstr "package launchable-activity"

获取结果:

{  "platformName": "Android",  "deviceName": "127.0.0.1:62001",  "appPackage": "com.xunmeng.pinduoduo",  "appActivity": "com.xunmeng.pinduoduo.ui.activity.MainFrameActivity"}

点击启动:

显示正在启动:

启动完毕显示:

启动完毕,剩下的就是常用与其他操作一样:

六、简单 demo 示例

    import io.appium.java_client.AppiumDriver;
    import io.appium.java_client.TouchAction;
    import io.appium.java_client.android.AndroidDriver;
    import io.appium.java_client.android.AndroidElement;
    import io.appium.java_client.android.AndroidKeyCode;
    import io.appium.java_client.functions.ExpectedCondition;
    import io.appium.java_client.remote.AndroidMobileCapabilityType;
    import io.appium.java_client.remote.MobileCapabilityType;
    import io.appium.java_client.touch.LongPressOptions;
    import io.appium.java_client.touch.WaitOptions;
    import io.appium.java_client.touch.offset.PointOption;
    import org.apache.commons.io.FileUtils;
    import org.openqa.selenium.*;
    import org.openqa.selenium.remote.DesiredCapabilities;
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import org.openqa.selenium.support.ui.WebDriverWait;
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;


    /**
     * @author 7DGroup
     * @Title: Bases
     * @Description: 安装初始化类
     * @date 2019/11/20 / 22:34
     */

    public class DriverBase {


        public static AndroidDriver<AndroidElement> driver;

        /**
         * @param port :服务器启动的端口号,系统自动获取
         * @param udid :手机设备号:系统自动化获取
         * @param apk  :自动化运行的APK包,系统会根据该地址获取包名与actiber
         * @param flag :true 卸掉有重新安装与运行后自动化卸掉包。false 直接安装运行
         * @return

         */

        public static AndroidDriver<AndroidElement> initDriver(String port, String udid, String apk, boolean flag) {
            ArrayList<String> packAct = OperationalCmd.getPackAct(apk);
    //        File app = new File(".\\\\apk\\\\20171026.apk");
            DesiredCapabilities caps = new DesiredCapabilities();
            //自动安装
            if (flag) {
                caps.setCapability(MobileCapabilityType.APP, apk);
                //结束后会卸载程序
                caps.setCapability(MobileCapabilityType.FULL_RESET, AndroidCapabilityType.FULL_RESET);
            }

            caps.setCapability(AndroidMobileCapabilityType.APPLICATION_NAME, udid);
            //PLATFORM_NAME: 平台名称
            caps.setCapability(AndroidMobileCapabilityType.PLATFORM_NAME, AndroidCapabilityType.PLATFORM_NAME);
           
           //UDID:设置操作手机的唯一标识,android手机可以通过adb devices查看
            caps.setCapability(MobileCapabilityType.DEVICE_NAME, udid);
           
           //NEW_COMMAND_TIMEOUT: appium server和脚本之间的 session超时时间
            caps.setCapability(AndroidCapabilityType.NEW_COMMAND_TIMEOUT, AndroidCapabilityType.NEW_COMMAND_TIMEOUT);
            
            //APP_PACKAG:Android应用的包名
            caps.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, packAct.get(0));

            //APP_ACTIVITY :启动app的起始activity
            caps.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, packAct.get(1));

            //UNICODE_KEYBOARD:1、中文输入不支持,2、不用它键盘会弹出来,说不定会影响下一步操作.需要注意设置后,需要将手机的输入法进行修改
            caps.setCapability(AndroidMobileCapabilityType.UNICODE_KEYBOARD, AndroidCapabilityType.UNICODE_KEY_BOARD);

            //Reset_KEYBOARD:是否重置输入法
            caps.setCapability(AndroidMobileCapabilityType.RESET_KEYBOARD, AndroidCapabilityType.RESET_KEY_BOARD);

            //NO_SIGN:跳过检查和对应用进行 debug 签名的
            caps.setCapability(AndroidMobileCapabilityType.NO_SIGN, AndroidCapabilityType.NO_SIGN);

            try {
                //appium测试服务的地址
                String serverUrl = "http://127.0.0.1";
                driver = new AndroidDriver<>(new URL(serverUrl +以上是关于移动端自动测试工具 Appium 快速入门的主要内容,如果未能解决你的问题,请参考以下文章

移动端自动化 AutoJS 快速入门指南

移动端自动化测试Appium从入门到项目实战Python版 学习 教程

移动测试之appium+python 入门代码

移动测试之appium+python 入门代码

appium 移动端自动化测试一

关于移动端自动化测试-Appium的搭建