Appium自动化测试框架搭建

Appium自动化测试框架搭建_第1张图片

 

Appium自动化测试框架搭建_第2张图片

 

1.在utils包中创建一个AppiumUtil类,这个类是对appium api进行封装的。

代码如下:

package utils;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.ITestResult;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MultiTouchAction;
import io.appium.java_client.NoSuchContextException;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;

/**
 * @author young
 * @description appium api封装
 * */

public class AppiumUtil {

    public  AppiumDriver driver;
    public     ITestResult it;
    /**定义日志输出对象*/
    public static Logger logger = Logger.getLogger(AppiumUtil.class);

    /**获取driver
     * @throws  */
    public AppiumDriver getDriver(String url,DesiredCapabilities capabilities,String platform){

            if(platform.equalsIgnoreCase("android")){
            try {
                driver = new AndroidDriver(new URL(url), capabilities);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            }else if(platform.equalsIgnoreCase("ios")){
                try {
                    driver = new IOSDriver (new URL(url),capabilities);
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }else{

            }
            return driver;

    }

    /**退出app*/
    public void closeApp(String appName){
        driver.closeApp();
        logger.info(appName+"已经关闭");
    }

    /**退出移动浏览器*/
    public void quit(){
        driver.quit();
        logger.info("driver已被清理");
    }
    /**通过By对象 去查找某个元素*/
    public WebElement findElement(By by){
        return driver.findElement(by);
    }

    /**
     * 通过By对象 去查找一组元素
     * */
    public List findElements(By by) {
        return driver.findElements(by);
    }

    /**清空元素内容*/
    public void clear(By byElement){
        WebElement element = findElement(byElement);
        element.clear();
        logger.info("清空元素:"+getLocatorByElement(element, ">")+"上的内容");
    }

    /**输入内容*/
    public void typeContent(By byElement,String str){
        WebElement element = findElement(byElement);
        element.sendKeys(str);
        logger.info("在元素:"+getLocatorByElement(element, ">")+"输入内容:"+str);
    }
    /**点击*/
    public void click(By byElement){
        WebElement element = findElement(byElement);
        try{
        element.click();
        logger.info("点击元素:"+getLocatorByElement(element, ">"));
        }catch(Exception e){
            logger.error("点击元素:"+getLocatorByElement(element, ">")+"失败", e);
            Assert.fail("点击元素:"+getLocatorByElement(element, ">")+"失败", e);
        }

    }

    /**查找一个元素 - appium新增的查找元素方法*/
    public WebElement findElement(String locateWay,String locateValue){
        WebElement element = null;
    switch(locateWay){

    case "AccessibilityId":
        element = driver.findElementByAccessibilityId(locateValue);
        break;
//    case "AndroidUIAutomator":
//        element = driver.findElementByAndroidUIAutomator(locateValue);
//            break;
    case "ClassName":
        element = driver.findElementByClassName(locateValue);
        break;
    case "CSS":
        element = driver.findElementByCssSelector(locateValue);
        break;
    case "ID":
        element = driver.findElementById(locateValue);
        break;
    case "LinkText":
        element = driver.findElementByLinkText(locateValue);
        break;
    case "Name":
        element = driver.findElementByName(locateValue);
        break;
    case "PartialLinkText":
        element = driver.findElementByPartialLinkText(locateValue);
        break;
    case "TagName":
        element = driver.findElementByTagName(locateValue);
        break;
    case "Xpath":
        element = driver.findElementByXPath(locateValue);
        break;
    default:
        logger.error("定位方式:"+locateWay+"不被支持");
        Assert.fail("定位方式:"+locateWay+"不被支持");

        }
    return element;

    }

    /**查找一组元素 - appium新增的查找元素方法*/
    public List findElements(String locateWay,String locateValue){
        List element=null;
    switch(locateWay){

    case "AccessibilityId":
        element = driver.findElementsByAccessibilityId(locateValue);
        break;
//    case "AndroidUIAutomator":
//        element = driver.findElementsByAndroidUIAutomator(locateValue);
//            break;
    case "ClassName":
        element = driver.findElementsByClassName(locateValue);
        break;
    case "CSS":
        element = driver.findElementsByCssSelector(locateValue);
        break;
    case "ID":
        element = driver.findElementsById(locateValue);
        break;
    case "LinkText":
        element = driver.findElementsByLinkText(locateValue);
        break;
    case "Name":
        element = driver.findElementsByName(locateValue);
        break;
    case "PartialLinkText":
        element = driver.findElementsByPartialLinkText(locateValue);
        break;
    case "TagName":
        element = driver.findElementsByTagName(locateValue);
        break;
    case "Xpath":
        element = driver.findElementsByXPath(locateValue);
        break;
    default:
        logger.error("定位方式:"+locateWay+"不被支持");
        Assert.fail("定位方式:"+locateWay+"不被支持");

        }
    return element;

    }

    /**获取文本1*/
    public String getText(By by){
        return findElement(by).getText().trim();
    }


    /**获取文本2*/
    public String getText(String locateWay,String locateValue){
        String str="";
    switch(locateWay){

    case "AccessibilityId":
        str = driver.findElementByAccessibilityId(locateValue).getText().trim();
        break;
//    case "AndroidUIAutomator":
//        str = driver.findElementByAndroidUIAutomator(locateValue).getText().trim();
//            break;
    case "ClassName":
        str = driver.findElementByClassName(locateValue).getText().trim();
        break;
    case "CSS":
        str = driver.findElementByCssSelector(locateValue).getText().trim();
        break;
    case "ID":
        str = driver.findElementById(locateValue).getText().trim();
        break;
    case "LinkText":
        str = driver.findElementByLinkText(locateValue).getText().trim();
        break;
    case "Name":
        str = driver.findElementByName(locateValue).getText().trim();
        break;
    case "PartialLinkText":
        str = driver.findElementByPartialLinkText(locateValue).getText().trim();
        break;
    case "TagName":
        str = driver.findElementByTagName(locateValue).getText().trim();
        break;
    case "Xpath":
        str = driver.findElementByXPath(locateValue).getText().trim();
        break;
    default:
        logger.error("定位方式:"+locateWay+"不被支持");
        Assert.fail("定位方式:"+locateWay+"不被支持");

        }
    return str;

    }

    /**提交*/
    public void submit(By by){
        WebElement element=findElement(by);
        try{
            element.submit();
        }catch(Exception e){
            logger.error("在元素:"+getLocatorByElement(element, ">")+"做的提交操作失败",e);
            Assert.fail("在元素:"+getLocatorByElement(element, ">")+"做的提交操作失败",e);
        }
        logger.info("在元素:"+getLocatorByElement(element, ">")+"做了提交操作");
    }

    /**
     * 获得webview页面的标题
     * */
    public String getTitle() {
        return driver.getTitle();
    }

    /**
     * 获得元素 属性的文本
     * */
    public String getAttributeText(By elementLocator, String attribute) {
        return findElement(elementLocator).getAttribute(attribute).trim();
    }

    /**
     * 在给定的时间内去查找元素,如果没找到则超时,抛出异常
     * */
    public void waitForElementToLoad(int elementTimeOut, final By By) {
        logger.info("开始查找元素[" + By + "]");
        try {
            (new WebDriverWait(driver, elementTimeOut)).until(new ExpectedCondition() {

                public Boolean apply(WebDriver driver) {
                    WebElement element = driver.findElement(By);
                    return element.isDisplayed();
                }
            });
        } catch (TimeoutException e) {
            logger.error("超时!! " + elementTimeOut + " 秒之后还没找到元素 [" + By + "]");
            Assert.fail("超时!! " + elementTimeOut + " 秒之后还没找到元素 [" + By + "]");

        }
        logger.info("找到了元素 [" + By + "]");
    }

    /**
     * 判断文本是不是和需求要求的文本一致
     * **/
    public void isTextCorrect(String actual, String expected) {
        try {
            Assert.assertEquals(actual, expected);
        } catch (AssertionError e) {
            logger.error("期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");
            Assert.fail("期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");

        }
        logger.info("找到了期望的文字: [" + expected + "]");

    }

    /**
     * 暂停当前用例的执行,暂停的时间为:sleepTime
     * */
    public void pause(int sleepTime) {
        if (sleepTime <= 0) {
            return;
        }
        try {
            TimeUnit.SECONDS.sleep(sleepTime);
            logger.info("暂停:"+sleepTime+"秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }



    /** 根据元素来获取此元素的定位值 */
    public String getLocatorByElement(WebElement element, String expectText) {
        String text = element.toString();
        String expect = null;
        try {
            expect = text.substring(text.indexOf(expectText) + 1, text.length() - 1);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("failed to find the string [" + expectText + "]");

        }

        return expect;

    }


    /**
     * 判断实际文本时候包含期望文本
     *
     * @param actual
     *            实际文本
     * @param expect
     *            期望文本
     */
    public void isContains(String actual, String expect) {
        try {
            Assert.assertTrue(actual.contains(expect));
        } catch (AssertionError e) {
            logger.error("The [" + actual + "] is not contains [" + expect + "]");
            Assert.fail("The [" + actual + "] is not contains [" + expect + "]");
        }
        logger.info("The [" + actual + "] is contains [" + expect + "]");
    }

    /**跳转到webview页面*/
    public void switchWebview(int index){
        Set contexts = driver.getContextHandles();
        for (String context : contexts) {
        System.out.println(context);
        //打印出来看看有哪些context
        }
        driver.context((String) contexts.toArray()[index]);

    }


    /**跳转到webview页面*/
    public void switchWebview(String contextName){
        try{
        Set contexts = driver.getContextHandles();
        for (String context : contexts) {
        System.out.println(context);
        //打印出来看看有哪些context
        }
        driver.context(contextName);
        }catch(NoSuchContextException nce){
            logger.error("没有这个context:"+contextName, nce);
            Assert.fail("没有这个context:"+contextName, nce);
        }

    }


    /**
     * 执行JavaScript 方法
     * */
    public void executeJS(String js) {
        ((JavascriptExecutor) driver).executeScript(js);
        logger.info("执行JavaScript语句:[" + js + "]");
    }

    /**
     * 执行JavaScript 方法和对象
     * 用法:seleniumUtil.executeJS("arguments[0].click();", seleniumUtil.findElementBy(MyOrdersPage.MOP_TAB_ORDERCLOSE));
     * */
    public void executeJS(String js, Object... args) {
        ((JavascriptExecutor) driver).executeScript(js, args);
        logger.info("执行JavaScript语句:[" + js + "]");
    }

    /**检查元素是不是存在*/
    public  boolean doesElementsExist(By byElement){
        try{
        findElement(byElement);
        return true;
        }catch(NoSuchElementException nee){

            return false;
        }


    }

    /**长按操作*/
    public void longPress(By by){
        TouchAction tAction=new TouchAction(driver);
        tAction.longPress(findElement(by)).perform();
    }

    /**滑动*/
    public void swipe(int beginX,int beginY,int endX,int endY){
        TouchAction tAction=new TouchAction(driver);
        try{
        tAction.press(beginX,beginY).moveTo(endX,endY).release().perform();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    /**滚动 - 根据文本模糊匹配*/
    public void scroll(String text){
        driver.scrollTo(text);
    }

    /**滚动 - 根据文本精准匹配*/
    public WebElement scrollExact(String text){
        return driver.scrollToExact(text);
    }

    /**拖拽操作*/
    public void DragAndDrop(By dragElement,By dropElement){
        TouchAction act=new TouchAction(driver);
        act.press(findElement(dragElement)).perform();
        act.moveTo(findElement(dropElement)).release().perform();
    }

    /**放大和缩小*/
    public void zoomAndPinch(int beginX,int beginY,int endX,int endY){
       int scrHeight = driver.manage().window().getSize().getHeight();
       int scrWidth = driver.manage().window().getSize().getWidth();
        MultiTouchAction multiTouch = new MultiTouchAction(driver);
        TouchAction tAction0 = new TouchAction(driver);
        TouchAction tAction1 = new TouchAction(driver);
        tAction0.press(scrWidth/2,scrHeight/2).waitAction(1000).moveTo(beginX,beginY).release();
        tAction1.press(scrWidth/2,scrHeight/2+40).waitAction(1000).moveTo(endX,endY).release();
        multiTouch.add(tAction0).add(tAction1);
        multiTouch.perform();

    }

    /**app置于后台运行*/
    public void runBackgound(int runTimes){
        driver.runAppInBackground(runTimes);

    }

    /**收起键盘*/
    public void hideKeyboard(){
        driver.hideKeyboard();
        logger.info("虚拟键盘已经收起");

    }

    /**安装app*/
    public void instalApp(String appPath){
        try{
        driver.installApp(appPath);
        }catch(Exception e){
            logger.error("app安装失败",e);
            Assert.fail("app安装失败",e);
        }
    }

    /**app是否安装*/
    public boolean isAppInstalled(String appPackage){

        if(driver.isAppInstalled(appPackage)){
            logger.info(appPackage+":已经安装");
            return true;
        }else {
            logger.info(appPackage+":未安装");
            return false;
        }
    }

    /**页面过长时候滑动页面 window.scrollTo(左边距,上边距); */
    public void scrollPage(int x,int y){
        String js ="window.scrollTo("+x+","+y+");";
        ((JavascriptExecutor)driver).executeScript(js);
    }



}

2.因为要显示log4日志,在pom.xml中加入jar包类:


log4j
log4j
1.2.16
provided

3.加载完成后要创建一个处理log类,名为:LogConfiguration类,代码如下

package com.young.appiumcombat.utils;
import java.util.Properties;

import org.apache.log4j.PropertyConfigurator;
/**
 * @author young
 * @decription 动态生成各个模块中的每条用例的日志,运行完成用例之后请到result/log目录下查看
 * */
public class LogConfiguration {

        public static void initLog(String fileName){
            //获取到模块名字
            String founctionName = getFunctionName(fileName);
            //声明日志文件存储路径以及文件名、格式
            final String logFilePath  = "./result/logs/"+founctionName+"/"+fileName+".log";
            Properties prop = new Properties();
            //配置日志输出的格式
            prop.setProperty("log4j.rootLogger","info, toConsole, toFile");
            prop.setProperty("log4j.appender.file.encoding","UTF-8" );
            prop.setProperty("log4j.appender.toConsole","org.apache.log4j.ConsoleAppender");
            prop.setProperty("log4j.appender.toConsole.Target","System.out");
            prop.setProperty("log4j.appender.toConsole.layout","org.apache.log4j.PatternLayout ");
            prop.setProperty("log4j.appender.toConsole.layout.ConversionPattern","[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
            prop.setProperty("log4j.appender.toFile", "org.apache.log4j.DailyRollingFileAppender");
            prop.setProperty("log4j.appender.toFile.file", logFilePath);
            prop.setProperty("log4j.appender.toFile.append", "false");
            prop.setProperty("log4j.appender.toFile.Threshold", "info");
            prop.setProperty("log4j.appender.toFile.layout", "org.apache.log4j.PatternLayout");
            prop.setProperty("log4j.appender.toFile.layout.ConversionPattern", "[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
            //使配置生效
            PropertyConfigurator.configure(prop);

        }


        /**取得模块名字*/
        public static String getFunctionName(String fileName){
            String functionName = null;
            int firstUndelineIndex = fileName.indexOf("_");
            functionName = fileName.substring(0, firstUndelineIndex-4);
            return functionName;

    }


}

4.因为我们要处理android和ios的项目,更方便处理兼容,在Utils中创建一个SelectDriver类,代码如下:

package com.young.appiumcombat.utils;
import java.io.File;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.Assert;
import org.testng.ITestContext;

import io.appium.java_client.AppiumDriver;
/**
 * @author Young
 * @description 根据测试平台的不同生成不同的driver 比如AndroidDriver 或者是IOSDriver
 *
 * */

public class SelectDriver {
    //声明driver
    public  AppiumDriver driver;
    //声明DesiredCapabilities
    public DesiredCapabilities capabilities;
    //声明ITestContext,用于获取testng配置文件内容
    public ITestContext testContext;
    //appium server地址
    public String serverURL;
    //测试引擎名字
    public String automationName;
    //测试平台名字
    public String platformName;
    //测试平台版本号
    public String platformVersion;
    //设备名字
    public String deviceName;
    //ios app的路径
    public String iosAppPath;
    //android app路径
    public String androidAppPath;
    //android app的 package
    public String appPackage;
    //android app的activity
    public String appActivity;
    //安卓独有 - 是否使用unicode键盘,使用此键盘可以输入中文字符
    public boolean unicodeKeyboard;
    //android独有 - 是否重置键盘,如果设置了unicodeKeyboard键盘,可以将此参数设置为true,然后键盘会重置为系统默认的
    public boolean resetKeyboard;
    //是否覆盖已有的seesssion,这个用于多用例执行,如果不设置的话,会提示前一个session还没有结束,用例就不能继续执行了
    public boolean sessionOverride;
    //暂停的等待时间
    public int sleepTime;
    //元素等待超时时间
    public int elementTimeOut;
    //app文件路径,主要存储的是app的名字
    public String appFilePath;
    //webview的名字或者叫标识符,一般以WEBVIEW开头,例如WEBVIEW_com.microsoft.bing
    public final static String WEBVIEW_NAME = null;
    //原生app的名字或者标识符,一般是NATIVE_APP
    public final static String NATIVEAPP_NAME = null;
    //实例化本类的日志输出对象
    public static Logger logger = Logger.getLogger(SelectDriver.class);

    public  AppiumDriver selectDriver(ITestContext context,AppiumUtil appiumUtil){
          //通过testng的xml文件获取serverURL参数值,并赋给  serverURL变量
          serverURL = context.getCurrentXmlTest().getParameter("serverURL");
          //通过testng的xml文件获取automationName参数值,并赋给  automationName变量
          automationName = context.getCurrentXmlTest().getParameter("automationName");
          //通过testng的xml文件获取platformName参数值,并赋给  platformName变量
          platformName = context.getCurrentXmlTest().getParameter("platformName");
          //通过testng的xml文件获取platformVersion参数值,并赋给  platformVersion变量
          platformVersion = context.getCurrentXmlTest().getParameter("platformVersion");
          //通过testng的xml文件获取deviceName参数值,并赋给  deviceName变量
          deviceName = context.getCurrentXmlTest().getParameter("deviceName");
          //通过testng的xml文件获取androidAppPath参数值,并赋给  androidAppPath变量
          androidAppPath = context.getCurrentXmlTest().getParameter("androidAppPath");
          //通过testng的xml文件获取iosAppPath参数值,并赋给  iosAppPath变量
          iosAppPath = context.getCurrentXmlTest().getParameter("iosAppPath");
          //通过testng的xml文件获取appPackage参数值,并赋给  appPackage变量
          appPackage = context.getCurrentXmlTest().getParameter("appPackage");
          //通过testng的xml文件获取appActivity参数值,并赋给  appActivity变量
          appActivity = context.getCurrentXmlTest().getParameter("appActivity");
          //通过testng的xml文件获取unicodeKeyboard参数值,并赋给  unicodeKeyboard变量
          unicodeKeyboard = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("unicodeKeyboard"));
          //通过testng的xml文件获取resetKeyboard参数值,并赋给  resetKeyboard变量
          resetKeyboard = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("resetKeyboard"));
          //通过testng的xml文件获取sessionOverride参数值,并赋给  sessionOverride变量
          sessionOverride = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("sessionOverride"));
          //通过testng的xml文件获取sleepTime参数值,并赋给  sleepTime变量
          sleepTime = Integer.valueOf(context.getCurrentXmlTest().getParameter("sleepTime"));
          //通过testng的xml文件获取elementTimeOut参数值,并赋给  elementTimeOut变量
          elementTimeOut = Integer.valueOf(context.getCurrentXmlTest().getParameter("elementTimeOut"));
          //通过testng的xml文件获取appFilePath参数值,并赋给  appFilePath变量
          appFilePath = context.getCurrentXmlTest().getParameter("appFilePath");
          this.testContext = context;
          capabilities = new DesiredCapabilities();
          //告诉测试程序,当前项目目录在哪里
          File classpathRoot = new File(System.getProperty("user.dir"));
          //设置capability,以便和appium创建session
          capabilities.setCapability("platformName",platformName);
          capabilities.setCapability("platformVersion",platformVersion);
          capabilities.setCapability("deviceName",deviceName);
          capabilities.setCapability("sessionOverride", sessionOverride);
          //如果测试平台是android的话,执行下面这个if语句内容
        if(platformName.equalsIgnoreCase("android")){
                /**
                 * 设置和android  测试相关的capability并实例化driver对象
                 * */
              File app = new File(classpathRoot, androidAppPath);
              capabilities.setCapability("app", app.getAbsolutePath());
              capabilities.setCapability("unicodeKeyboard", unicodeKeyboard);
              capabilities.setCapability("resetKeyboard", resetKeyboard);
              capabilities.setCapability("automationName",automationName);
              capabilities.setCapability("appPackage", appPackage);
              capabilities.setCapability("appActivity", appActivity);
              driver = appiumUtil.getDriver(serverURL, capabilities,platformName);
              testContext.setAttribute("APPIUM_DRIVER", driver);
              logger.info(PropertiesDataProvider.getTestData(appFilePath, appPackage)+"已经启动");
              driver.manage().timeouts().implicitlyWait(elementTimeOut, TimeUnit.SECONDS);
              return driver;

              //如果测试平台是ios的话,执行下面这个if语句内容
        }else if(platformName.equalsIgnoreCase("ios")){
            /**
             * 设置和ios  测试相关的capability并实例化driver对象
             * */
              File app = new File(classpathRoot, iosAppPath);
              capabilities.setCapability("app", app.getAbsolutePath());
              //ios设置自动接收系统alert,注意IOS弹出的alert,APPIUM可以自动处理掉,支持ios8以上系统
              capabilities.setCapability("autoAcceptAlerts", true);
              driver = appiumUtil.getDriver(serverURL, capabilities,platformName);
              testContext.setAttribute("APPIUM_DRIVER", driver);
              driver.manage().timeouts().implicitlyWait(elementTimeOut,TimeUnit.SECONDS);

        }else{
        logger.error("初始化driver失败");
        Assert.fail("初始化driver失败");
        }

        //最后返回dirver对象
        return driver;


    }


}

5.有时候我们不想把太多的配置放到testng.xml中,我们就需要根据key读取属性文件里面的value值,现在res/下创建properties,里面有app.properties 和config.properties 两个文本,然后在utils下写一个方法获取value的值,类为:PropertiesDataProvider,代码如下:

package com.young.appiumcombat.utils;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;

/**
 * @author young
 * @Desription 从.properties文件中读取相关测试数据
* * */ public class PropertiesDataProvider { public static String getTestData(String configFilePath, String key) { Configuration config = null; try { config = new PropertiesConfiguration(configFilePath); } catch (ConfigurationException e) { e.printStackTrace(); } return String.valueOf(config.getProperty(key)); } }

6.html报告插件

哈,我们使用https://blog.csdn.net/A_Kaka/article/details/106988528这里的arrow的报告插件,在将CONFIGFILE值修改成res/properties目录下的 config.properties,代码如下:

1 retrycount=0
2 sourcecodedir=com/young/appiumcombat/testcases
3 sourcecodeencoding=UTF-8

另:笔者建立了一个软件测试技术交流群1125760266,欢迎大家加入进来一起学习,探讨

你可能感兴趣的:(Appium自动化测试框架搭建)