IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 移动端自动测试工具 Appium 快速入门 -> 正文阅读

[开发测试]移动端自动测试工具 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 + ":" + port + "/wd/hub"), caps);

            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            return driver;
        }
    }

AndroidCapabilityType:

    import java.io.File;

    /**
     * @author 7DGroup
     * @Title: AndroidCapabilityType
     * @Description:功能配置类
     * @date 2019/11/20 / 22:01
     */

    public class AndroidCapabilityType {

        private AndroidCapabilityType() {
        }

        public static final boolean NO_SIGN = true;
        public static final boolean UNICODE_KEY_BOARD = true;
        public static final boolean RESET_KEY_BOARD = true;

        /**
         * waitElement 时的最长等待时间
         */

        public static final String NEW_COMMAND_TIMEOUT = "600";
        public static final String PLATFORM_NAME = "Android";
        public static final boolean FULL_RESET = true;

        /**
         * 向上小滑动一步
         */

        public static final String APP_UP_SWIPE = "adb shell input touchscreen swipe 400 800 400 300";
        public static final String APP_GET_PACK_ACTIVITY = "aapt d badging pathapk |findstr \"package launchable-activity\"";

        /**
         * 重启应用
         */

        public static final String RESTAPK = "adb -s 127.0.0.1 shell am start -n WelcomeActivityPama";

        /**adb*/
        /**
         * 根据包名得到进程
         */

        public static final String GETAPPPACKAGEPID = "adb shell ps | grep ";

        /**
         * 打开指定app
         */

        public static final String OPEN_APP = "shell am start -n packagename activity";

        /**
         * 本地存储截屏图片的目录,(注意配置时的格式)
         */


        public static final String LOCAL_SCREEN_FILE_URL = getpathlocal();

        /**
         * 获取目录工程路径
         *
         * @return
         */

        public static String getpathlocal() {
            File f = new File("");
            String logpath = f.getAbsolutePath() + "/test-output/html/screenshots";
            File file = new File(logpath);
            if (!file.exists()) {
                f.mkdirs();
            }
            return file.toString();

        }

        /**
         * 本地存储截屏图片的格式
         */

        public static final String LOCAL_SCREEN_FILE_FORMAT = ".png";

获取包名工具 getPackAct:

  /**
     * 获取包名与 APP_ACTIVITY
     *
     * @param path
     * @return
     */
    public static ArrayList<String> getPackAct(String path) {
        ArrayList<String> list = new ArrayList<>();
        try {
            List<String> execute = execute(AndroidCapabilityType.APP_GET_PACK_ACTIVITY.replace("pathapk", path), true);
            for (String s : execute) {
                int i = s.indexOf("name='");
                int i1 = s.indexOf("' versionCode=");
                if (s.contains("versionCode")) {
                    String substring = s.substring(i + 6, i1);
                    list.add(substring);
                } else {
                    int i2 = s.indexOf("'  label='");
                    String substring = s.substring(i + 6, i2);
                    list.add(substring);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

可以使用下面做启动:

 public static AndroidDriver<?> initDriver() throws Exception {
        File app = new File(".\\apk\\20171026.apk");
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, "xxx");
        //caps.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());  //自动安装
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Appium");
        caps.setCapability(MobileCapabilityType.UDID, "127.0.0.1:62001");
        caps.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 600);
        //caps.setCapability(MobileCapabilityType.FULL_RESET, true); //结束后会卸载程序
        caps.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.xunmeng.pinduoduo");
        caps.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, "com.xunmeng.pinduoduo.ui.activity.MainFrameActivit");
        caps.setCapability(AndroidMobileCapabilityType.UNICODE_KEYBOARD, true);
        caps.setCapability(AndroidMobileCapabilityType.RESET_KEYBOARD, true);
        caps.setCapability(AndroidMobileCapabilityType.NO_SIGN, true);
        driver = new AndroidDriver<>(
                new URL("http://127.0.0.1:4723/wd/hub"), caps);
        return driver;

    }

测试报告:
部分代码(如果需要请再群@)

    /**
     * @author 7DGroup
     * @Title: ReporterListener
     * @Description: 自定义报告监听类
     * @date 2019/11/21 / 18:56
     */

    public class ReporterListener implements IReporter, ITestListener {
        private static final Logger log = LoggerFactory.getLogger(DriverBase.class);
        private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");

        @Override
        public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
            List<ITestResult> list = new LinkedList<>();
            Date startDate = new Date();
            Date endDate = new Date();

            int TOTAL = 0;
            int SUCCESS = 1;
            int FAILED = 0;
            int ERROR = 0;
            int SKIPPED = 0;
            for (ISuite suite : suites) {
                Map<String, ISuiteResult> suiteResults = suite.getResults();
                for (ISuiteResult suiteResult : suiteResults.values()) {
                    ITestContext testContext = suiteResult.getTestContext();
                    startDate = startDate.getTime() > testContext.getStartDate().getTime() ? testContext.getStartDate() : startDate;
                    if (endDate == null) {
                        endDate = testContext.getEndDate();
                    } else {
                        endDate = endDate.getTime() < testContext.getEndDate().getTime() ? testContext.getEndDate() : endDate;
                    }

                    IResultMap passedTests = testContext.getPassedTests();
                    IResultMap failedTests = testContext.getFailedTests();
                    IResultMap skippedTests = testContext.getSkippedTests();
                    IResultMap failedConfig = testContext.getFailedConfigurations();

                    SUCCESS += passedTests.size();
                    FAILED += failedTests.size();
                    SKIPPED += skippedTests.size();
                    ERROR += failedConfig.size();

                    list.addAll(this.listTestResult(passedTests));
                    list.addAll(this.listTestResult(failedTests));
                    list.addAll(this.listTestResult(skippedTests));
                    list.addAll(this.listTestResult(failedConfig));
                }
            }

            /* 计算总数 */

            TOTAL = SUCCESS + FAILED + SKIPPED + ERROR;

            this.sort(list);
            Map<String, TestResultCollection> collections = this.parse(list);
            VelocityContext context = new VelocityContext();

            context.put("TOTAL", TOTAL);
            context.put("mobileModel", OperationalCmd.getMobileModel());
            context.put("versionName", OperationalCmd.getVersionNameInfo());
            context.put("SUCCESS", SUCCESS);
            context.put("FAILED", FAILED);
            context.put("ERROR", ERROR);
            context.put("SKIPPED", SKIPPED);
            context.put("startTime", ReporterListener.formatDate(startDate.getTime()) + "<--->" + ReporterListener.formatDate(endDate.getTime()));
            context.put("DURATION", ReporterListener.formatDuration(endDate.getTime() - startDate.getTime()));
            context.put("results", collections);
            write(context, outputDirectory);

        }


        /**
         * 输出模板
         *
         * @param context
         * @param outputDirectory

         */

        private void write(VelocityContext context, String outputDirectory) {
            if (!new File(outputDirectory).exists()) {
                new File(outputDirectory).mkdirs();
            }

            //获取报告模板
            File f = new File("");
            String absolutePath = f.getAbsolutePath();
            String fileDir = absolutePath + "/template/";
            String reslutpath = outputDirectory + "/html/report" + ReporterListener.formateDate() + ".html";
            File outfile = new File(reslutpath);
            if (!outfile.exists()) {
                outfile.mkdirs();
            }

            try {
                //写文件
                VelocityEngine ve = new VelocityEngine();
                Properties p = new Properties();
                p.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, fileDir);
                p.setProperty(Velocity.ENCODING_DEFAULT, "utf-8");
                p.setProperty(Velocity.INPUT_ENCODING, "utf-8");
                ve.init(p);

                Template t = ve.getTemplate("reportnew.vm");

                //输出结果
                OutputStream out = new FileOutputStream(new File(reslutpath));
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));

                // 转换输出
                t.merge(context, writer);
                writer.flush();
                log.info("报告位置:" + reslutpath);

            } catch (IOException e) {
                e.printStackTrace();

            }

        }


        /**
         * 排序规则
         *

         * @param list

         */

        private void sort(List<ITestResult> list) {
            Collections.sort(list, new Comparator<ITestResult>() {
                @Override
                public int compare(ITestResult r1, ITestResult r2) {
                    if (r1.getStatus() < r2.getStatus()) {
                        return 1;
                    } else {
                        return -1;
                    }
                }
            });
        }

模板(部分代码):

<h2>详情</h2>
    #foreach($result in $results.entrySet())
        #set($item = $result.value)
    <table id="$result.key" class="details">
        <tr>
            <th>测试类</th>
            <td colspan="4">$result.key</td>
        </tr>
        <tr>
            <td>TOTAL: $item.totalSize</td>
            <td>SUCCESS: $item.successSize</td>
            <td>FAILED: $item.failedSize</td>
            <td>ERROR: $item.errorSize</td>
            <td>SKIPPED: $item.skippedSize</td>
        </tr>
        <tr>
            <th>Status</th>
            <th>Method</th>
            <th>Description</th>
            <th>Duration</th>
            <th>Detail</th>
        </tr>
        #foreach($testResult in $item.resultList)
            <tr>
                #if($testResult.status==1)
                    <th class="success" style="width:5em;">success
                    </td>
                #elseif($testResult.status==2)
                    <th class="failure" style="width:5em;">failure
                    </td>
                #elseif($testResult.status==3)
                    <th class="skipped" style="width:5em;">skipped
                    </td>
                #end
                <td>$testResult.testName</td>
                <td>${testResult.description}</td>
                <td>${testResult.duration} seconds</td>
                <td class="detail">
                ##                    <a class="button" href="#popup_log_${testResult.caseName}_${testResult.testName}">log</a>
                    <button type="button" class="btn btn-primary btn-lg" data-toggle="modal"
                            data-target="#popup_log_${testResult.caseName}_${testResult.testName}">
                        log
                    </button>
                    <!-- 日志模态框 -->
                    <div class="modal fade" id="popup_log_${testResult.caseName}_${testResult.testName}" tabindex="-1"
                         role="dialog" aria-labelledby="myModalLabel_${testResult.testName}">
                        <div class="modal-dialog" role="document">
                            <div class="modal-content">
                                <div class="modal-header">
                                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
                                            aria-hidden="true">&times;</span></button>
                                    <h4 class="modal-title" id="myModalLabel_${testResult.testName}">用例操作步骤</h4>
                                </div>
                                <div class="modal-body">
                                    <div style="overflow: auto">
                                        <table>
                                            <tr>
                                                <th>日志</th>
                                                <td>
                                                    #foreach($msg in $testResult.twooutparam)
                                                        <pre>$msg</pre>
                                                    #end
                                                </td>
                                            </tr>
                                            #if($testResult.status==2)
                                                <tr>
                                                    <th>异常</th>
                                                    <td>
                                                        <pre>$testResult.throwableTrace</pre>
                                                    </td>
                                                </tr>
                                            #end
                                        </table>
                                    </div>
                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </td>
            </tr>
        #end
    </table>
    #end

启动测试类:

        static AndroidDriver<AndroidElement> driver;


        /**
         * 初始化运行类
         *
         * @param udid
         * @param port
         * @throws Exception
         */

        @BeforeClass
        @Parameters({"udid", "port"})
        public void BeforeClass(String udid, String port) {
            Reporter.log("步骤1:启动appium与应用", true);
            LogUtil.info("---这是设备ID号-->" + udid);
            LogUtil.info("--这是运行端口--->" + port);

            //通过路径获取包名与APP_ACTIVITY
            String apk = "pinduoduov4.76.0_downcc.com.apk";
            driver = DriverBase.initDriver(port, udid, apk, true);
            driver.manage().timeouts().implicitlyWait(80, TimeUnit.SECONDS);
        }


        @Test
        public void T001() {
            LogUtil.info("启动");
            driver.findElement(By.id("com.xunmeng.pinduoduo:id/bo0")).click();
        }

使用 xml 启动:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="UI自动化" parallel="tests" thread-count="1">
    <listeners>
        <listener class-name="appout.reporter.ReporterListener"></listener>
    </listeners>
    <test name="M6TGLMA721108530">
        <parameter name="udid" value="M6TGLMA721108530"/>
        <parameter name="port" value="4723"/>
        <classes>
            <class name="appout.appcase.LoginTest"/>
        </classes>
    </test>
</suite>

命令号启动:
在这里插入图片描述

这样跑xml就能得到如下结果。

七、运行效果

在这里插入图片描述

log 弹出:
?

在这里插入图片描述

八、工程目录

在这里插入图片描述

注意:
如果在启动的时候有问题,自己微调下,大概大家只是看看而已,有问题到群里问或者联系@就行会单独指导怎么使用。
?

九、总结

使用 maven 建立项目,通过 tesng 做测试类与传参,以上简单介绍了环境部署,定位方式,启动类,报告类等方法。

主要的知识点:

  • Java
  • Testng
  • idea
  • appium
  • html

?

?

?

?

?

?

?

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-10-06 12:32:02  更:2021-10-06 12:33:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 2:22:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码