精华内容
下载资源
问答
  • Appium自动化测试框架

    2018-10-25 08:45:44
    测试框架的功能: 业务功能的封装 测试用例的封装 测试包的管理 截图处理 断言处理 日志获取 测试报告生成 数据驱动 数据配置:yaml配置、日志配置等   实现自动化的整体步骤: 框架设计图: 定义好每...

    测试框架的功能:

    1. 业务功能的封装
    2. 测试用例的封装
    3. 测试包的管理
    4. 截图处理
    5. 断言处理
    6. 日志获取
    7. 测试报告生成
    8. 数据驱动
    9. 数据配置:yaml配置、日志配置等

     

    实现自动化的整体步骤:

    框架设计图:

    定义好每个文件夹应该实现功能或存放的文件

    代码实现

    封装各个模块的代码, 例如封装desired_caps.py,loginViewpy等

    展开全文
  • 前面 Appium 自动化测试框架大概发的都差不多了,那么接下来分享有关 Appium 自动化测试框架的综合实践,想必有些小伙伴都等不及了吧! 1、driver配置封装 kyb_caps.yaml 配置表 ,主要是一些配置信息的封装。 代码...

    Time will tell.

    前面 Appium 自动化测试框架大概发的都差不多了,那么接下来分享有关 Appium 自动化测试框架的综合实践,想必有些小伙伴都等不及了吧!

    1、driver配置封装

    kyb_caps.yaml 配置表 ,主要是一些配置信息的封装。

    代码:

    platformName: Android
    #模拟器
    platformVersion: 5.1.1
    deviceName: 127.0.0.1:62025
    
    #mx4真机
    #platformVersion: 5.1
    #udid: 750BBKL22GDN
    #deviceName: MX4
    
    appname: kaoyan3.1.0.apk
    noReset: False
    unicodeKeyboard: True
    resetKeyboard: True
    
    appPackage: com.tal.kaoyan
    appActivity: com.tal.kaoyan.ui.activity.SplashActivity
    ip: 127.0.0.1
    port: 4723
    

    desired_caps.py,主要是用来读取配置文件的信息的封装。

    代码:

    # coding=utf-8
    # 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
    # 2.注释:包括记录创建时间,创建人,项目名称。
    # 3.导入模块
    
    from appium import webdriver
    import yaml
    import logging
    import logging.config
    import os
    
    CON_LOG='../config/log.conf'
    logging.config.fileConfig(CON_LOG)
    logging=logging.getLogger()
    
    def appium_desired():
        with open('../config/kyb_caps.yaml','r',encoding='utf-8') as file:
            data=yaml.load(file)
    
        desired_caps={}
        desired_caps['platformName']=data['platformName']
        desired_caps['platformVersion']=data['platformVersion']
        desired_caps['deviceName']=data['deviceName']
    
        base_dir = os.path.dirname(os.path.dirname(__file__))
        app_path = os.path.join(base_dir, 'app', data['appname'])
        desired_caps['app']=app_path
    
        desired_caps['appPackage']=data['appPackage']
        desired_caps['appActivity']=data['appActivity']
        desired_caps['noReset']=data['noReset']
    
    
        desired_caps['unicodeKeyboard']=data['unicodeKeyboard']
        desired_caps['resetKeyboard']=data['resetKeyboard']
    
        logging.info('start app...')
        driver=webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub',desired_caps)
        driver.implicitly_wait(8)
        return driver
    
    if __name__ == '__main__':
        appium_desired()
    
        # with open('../config/kyb_caps.yaml', 'r', encoding='utf-8') as file:
        #     data = yaml.load(file)
        #
        # base_dir=os.path.dirname(os.path.dirname(__file__))
        # print(os.path.dirname(__file__))
        # print(base_dir)
        #
        # app_path=os.path.join(base_dir,'app',data['appname'])
        # print(app_path)
    

    相对路径符号含义

    • “.” 表示当前目录

    • “…” 表示当前目录的上一级目录。

    • “./” 表示当前目录下的某个文件或文件夹,视后面跟着的名字而定

    • “…/” 表示当前目录上一级目录的文件或文件夹,视后面跟着的名字而定。

    2、基类封装

    baseView.py,主要是一些元素定位方法的封装。

    代码:

    # coding=utf-8
    # 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
    # 2.注释:包括记录创建时间,创建人,项目名称。
    # 3.导入模块
    
    class BaseView(object):
        def __init__(self,driver):
            self.driver=driver
    
        def find_element(self,*loc):
            return self.driver.find_element(*loc)
    
        def find_elements(self,*loc):
            return self.driver.find_elements(*loc)
    
        def get_window_size(self):
            return self.driver.get_window_size()
    
        def swipe(self,start_x, start_y, end_x, end_y, duration):
            return self.driver.swipe(start_x, start_y, end_x, end_y, duration)
    

    3、common 公共模块封装

    公共方法封装 : common_fun.py,主要是一些公共方法的封装。

    代码:

    # coding=utf-8
    # 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
    # 2.注释:包括记录创建时间,创建人,项目名称。
    # 3.导入模块
    
    from kyb_testProject.baseView.baseView import BaseView
    from kyb_testProject.common.desired_caps import appium_desired
    from selenium.common.exceptions import NoSuchElementException
    import logging
    from selenium.webdriver.common.by import By
    import time,os
    import csv
    
    class Common(BaseView):
        cancelBtn=(By.ID,'android:id/button2')
        skipBtn=(By.ID,'com.tal.kaoyan:id/tv_skip')
        wemedia_cacel=(By.ID,'com.tal.kaoyan:id/view_wemedia_cacel')
    
    
        def check_cancelBtn(self):
            logging.info('==========check_cancelBtn=========')
            try:
                cancelBtn = self.driver.find_element(*self.cancelBtn)
            except NoSuchElementException:
                logging.info('no cancelBtn')
            else:
                cancelBtn.click()
    
        def check_skipBtn(self):
            logging.info('=========check skipBtn=============')
    
            try:
                skipBtn = self.driver.find_element(*self.skipBtn)
            except NoSuchElementException:
                logging.info('no skipBtn')
            else:
                skipBtn.click()
    
        def get_size(self):
            x = self.driver.get_window_size()['width']
            y = self.driver.get_window_size()['height']
            return x, y
    
        def swipeLeft(self):
            logging.info('swipeLeft')
            l = self.get_size()
            x1 = int(l[0] * 0.9)
            y1 = int(l[1] * 0.5)
            x2 = int(l[0] * 0.1)
            self.swipe(x1, y1, x2, y1, 1000)
    
        def getTime(self):
            self.now=time.strftime("%Y-%m-%d %H_%M_%S")
            return self.now
    
        def getScreenShot(self,module):
            time=self.getTime()
            image_file=os.path.dirname(os.path.dirname(__file__))+'/screenshots/%s_%s.png' %(module,time)
    
            logging.info('get %s screenshot' %module)
            self.driver.get_screenshot_as_file(image_file)
    
        def check_market_ad(self):
            logging.info('====check_market_ad====')
            try:
                element=self.driver.find_element(*self.wemedia_cacel)
            except NoSuchElementException:
                pass
            else:
                logging.info('close market ad')
                element.click()
    
        def get_csv_data(self,csv_file,line):
            logging.info('=====get_csv_data======')
            with open(csv_file,'r',encoding='utf-8-sig') as file:
                reader=csv.reader(file)
                for index,row in enumerate(reader,1):
                    if index==line:
                        return row
    
    if __name__ == '__main__':
        # driver=appium_desired()
        # com=Common(driver)
        # com.check_cancelBtn()
        # # com.check_skipBtn()
        # com.swipeLeft()
        # com.getScreenShot('startApp')
    
        list = ["这", "是", "一个", "测试", "数据"]
        # for i in range(len(list)):
            # print(i, list[i])
    
        list1 = ["这", "是", "一个", "测试", "数据"]
        # for index, item in enumerate(list1):
        #     print(index, item)
    

    好了,今天的分享就到这里。如果你对更多内容、Python自动化软件测试、面试题感兴趣的话可以加入我们175317069一起学习。群里会有各项测试学习资源发放,更有行业深潜多年的技术人分析讲解。

    祝你能成为一名优秀的软件测试工程师!

    欢迎【点赞】、【评论】、【关注】~

    Time will tell.(时间会证明一切)

    展开全文
  • 项目名及简介此项目是在appium和Selenium开源工具封装而成的自动化app和web测试工具功能都是基于python3都是基于webdriver,大部分代码都可以通用,只是配置文件不一样APP监控了常用的men,cpu,fps数据维护用的YMAL...

    项目名及简介

    此项目是在appium和Selenium开源工具封装而成的自动化app和web测试工具

    功能

    都是基于python3

    都是基于webdriver,大部分代码都可以通用,只是配置文件不一样

    APP监控了常用的men,cpu,fps

    数据维护用的YMAL

    邮件发送excel的测试报告

    支持多设备andoird并行

    用法

    下载项目:

    git clone git@github.com:Louis-me/appiumn_auto.git

    配置devices.yaml

    appium:

    - devices: JTJ4C16331013562

    port: 4723

    config: node D:\app\Appium\node_modules\appium\bin\appium.js -p 4723 -bp 4733

    platformName: android

    - devices: MSM8926

    port: 4724

    config: node D:\app\Appium\node_modules\appium\bin\appium.js -p 4724 -bp 4734

    platformName: android

    yaml

    ---

    -

    element_info: cn.ibona.t1_beta:id/start_button

    find_type: by_id

    operate_type: click

    test_id: 1002

    test_intr: 登陆

    -

    element_info: cn.ibona.t1_beta:id/passwordEditText

    find_type: by_id

    operate_type: send_keys

    test_id: 1002

    text: 111111

    -

    element_info: cn.ibona.t1_beta:id/phoneNumberEditText

    find_type: by_id

    operate_type: send_keys

    text: 18576759587

    -

    element_info: cn.ibona.t1_beta:id/loginButton

    find_type: by_id

    operate_type: click

    -

    element_info: cn.ibona.t1_beta:id/toolbar

    find_type: by_id

    命名行运行:

    pyhton testRunner/runner.py

    使用截图

    运行方式

    c61b8b040039

    Paste_Image.png

    APP运行情况

    c61b8b040039

    Paste_Image.png

    结果展示

    c61b8b040039

    Paste_Image.png

    c61b8b040039

    Paste_Image.png

    c61b8b040039

    Paste_Image.png

    其他

    展开全文
  • Appium自动化测试框架示例

    千次阅读 2017-06-20 15:25:35
    Appium自动化测试框架本文依赖前面那篇Appium的配置环境,讲述一个比较通用的基于Appium的自动化测试项目框架,本人Android开发,本文视角会偏向于Android平台,由于Appium是跨平台的自动化测试工具,本文讲述的项目...

    Appium自动化测试框架

    本文依赖前面那篇Appium的配置环境,讲述一个比较通用的基于Appium的自动化测试项目框架,本人Android开发,本文视角会偏向于Android平台,由于Appium是跨平台的自动化测试工具,本文讲述的项目框架依然适用于iOS平台的自动化测试方案,iOS开发可以参考,再次感谢本文参考文章的作者,谢谢你们的辛勤付出,以下是参考文章的链接,小伙伴们也可以参考:


    正文开始

    一,项目的目录结构

    我们首先看一下这个测试项目的整个结构,每个目录的用途我会简要标明,然后一个一个文件讲述:
    这里写图片描述

    一,项目的主要代码文件

    首先,我们将项目中需要用到的jar配置到pom.xml,使用maven去下载管理,以下是pom.xml的内容

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>**your groupid**</groupId>
        <artifactId>**your artifactid**</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>aldb</name>
        <url>http://maven.apache.org</url>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>6.10</version>
                <scope>test</scope>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/junit/junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>io.appium</groupId>
                <artifactId>java-client</artifactId>
                <version>4.1.2</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.seleniumhq.selenium</groupId>
                        <artifactId>selenium-java</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- https://mvnrepository.com/artifact/commons-configuration/commons-configuration -->
            <dependency>
                <groupId>commons-configuration</groupId>
                <artifactId>commons-configuration</artifactId>
                <version>1.10</version>
            </dependency>
    
            <dependency>
                <groupId>net.sourceforge.jexcelapi</groupId>
                <artifactId>jxl</artifactId>
                <version>2.6.12</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- Includes the Sauce JUnit helper libraries -->
            <dependency>
                <groupId>com.saucelabs</groupId>
                <artifactId>sauce_junit</artifactId>
                <version>LATEST</version>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.thoughtworks.qdox/qdox -->
            <dependency>
                <groupId>com.thoughtworks.qdox</groupId>
                <artifactId>qdox</artifactId>
                <version>1.12.1</version>
                <scope>compile</scope>
            </dependency>
    
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.10-FINAL</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>6.0.5</version>
            </dependency>
            <dependency>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-java</artifactId>
                <version>2.53.0</version>
            </dependency>
            <dependency>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-remote-driver</artifactId>
                <version>2.53.0</version>
            </dependency>
            <dependency>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty</artifactId>
                <version>7.0.0.pre5</version>
            </dependency>
    
        </dependencies>
    
        <repositories>
            <repository>
                <id>saucelabs-repository</id>
                <url>https://repository-saucelabs.forge.cloudbees.com/release</url>
                <releases>
                    <enabled>true</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
        </repositories>
         <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>test-jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <!-- 经过测试 maven-compiler-plugin 插件版本请使用3.3,否则在jenkins上无法执行测试 -->
                    <version>3.3</version>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.19.1</version>
                    <configuration>
                        <argLine>-Dfile.encoding=UTF-8</argLine>
                        <argLine>-Xms1024m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=128m</argLine>
                        <forkMode>never</forkMode>
                        <suiteXmlFiles>
                            <suiteXmlFile>testng.xml</suiteXmlFile>
                        </suiteXmlFiles>
                        <reportsDirectory>./result/test-report</reportsDirectory>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <transformers>
                                    <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <mainClass>com.android.aldb.mySql</mainClass>
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins> 
        </build>
    </project>
    

    配置后可能需要一定时间下载,耐心等待

    因为是初次接触这个框架,我们还是用上篇的通讯录demo作为我们这个框架的学习素材,将apk包放入包目录:
    这里写图片描述

    准备各目录里的文件

    一,base目录

    这里写图片描述

    下面是BasePrepare的代码:

    public class BasePrepare {
        protected  AppiumDriver<WebElement> driver; 
        protected AppiumUtil appiumUtil;
        public static Logger logger = Logger.getLogger(BasePrepare.class);
        protected String platformName;
        protected String appFilePath;
        protected String appPackage;
        protected int elementTimeOut;
      @BeforeClass
      public void initTest(ITestContext context) throws MalformedURLException{
          //使log4j的配置生效,以便输出日志
          LogConfiguration.initLog(this.getClass().getSimpleName());
          //获取platform、appFilePath、appPackage的值,这个值是从testng的配置文件获取的
          if(null == context){
              logger.info("null == context");
          }else if(null == context.getCurrentXmlTest()){
              logger.info("null == context.getCurrentXmlTest()");
          }
          platformName = context.getCurrentXmlTest().getParameter("platformName");
          appFilePath = context.getCurrentXmlTest().getParameter("appFilePath");
          appPackage = context.getCurrentXmlTest().getParameter("appPackage");
          elementTimeOut = Integer.valueOf(context.getCurrentXmlTest().getParameter("elementTimeOut"));
          appiumUtil = new AppiumUtil();
          //调用SelectDriver类的selectDriver方法,生成driver对象
          driver = new SelectDriver().selectDriver(context,appiumUtil);
    
    
      }
    
      @AfterClass
      public void clenTest(){
          if(driver!=null){
    
              appiumUtil.closeApp(PropertiesDataProvider.getTestData(appFilePath, appPackage));//appium模式
             logger.info("请等待60秒,待下一个用例执行");
    
             try {
                 Thread.sleep(60000);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
             // driver.quit(); //selendroid 模式
          }else{
              Assert.fail("driver没有获得对象,退出操作失败");
    
    
          }
      }
    
        /**
         * 测试数据提供者 - 方法
         * */
        @DataProvider(name = "testData")
        public Iterator<Object[]> dataFortestMethod() throws IOException {
            String moduleName = null; // 模块的名字
            String caseNum = null; // 用例编号
            String className = this.getClass().getName();
            int dotIndexNum = className.indexOf("."); // 取得第一个.的index
            int underlineIndexNum = className.indexOf("_"); // 取得第一个_的index
    
            if (dotIndexNum > 0) {
                /**这里的calssName原始值大概是这样的:
                 * com.android.aldb.testcase.home.HomePage_002_UiCheck_Test
                 * 那么下面这段代码className.substring(33, className.lastIndexOf("."))是什么意思?substring方法参数有两个
                 * 一个开始位置,一个结束位置,33表示这个字符串的第33个位置,这个位置当前字符是l,className.lastIndexOf(".")表示返回这字符串最后一个.所在
                 * 的位置,它是38,那么className.substring(33, className.lastIndexOf("."))可以转换成:className.substring(33, 38),最终取得的值是login,
                 * 也就是moduleName的值
                 * 
                 * 
                 * */
                moduleName = className.substring(26, className.lastIndexOf(".")); // 取到模块的名称
            }
    
            if (underlineIndexNum > 0) {
                //这个分析方法和moduleName的分析方法一样
                caseNum = className.substring(underlineIndexNum + 1, underlineIndexNum + 4); // 取到用例编号
            }
            //将模块名称和用例的编号传给 ExcelDataProvider ,然后进行读取excel数据
            return new ExcelDataProvider(moduleName, caseNum);
        }
    
    }

    这里主要关注两个方法,第一个是initTest方法,里面主要的一段是

    appiumUtil = new AppiumUtil();
          //调用SelectDriver类的selectDriver方法,生成driver对象
          driver = new SelectDriver().selectDriver(context,appiumUtil);

    这个构建driver的方法主要是用配置文件里的参数来给driver做一个初始化的配置,在整个测试案例的生命周期里便可由driver来控制。第二个方法即调用driver.clossApp方法来结束整个流程,第三个方法对于我们这些初学者暂时可以不了解,其实看代码也很简单就是从Excel里读一些数据作为测试流程的数据源,所以测试案例的命名也是有一定规则的,具体可结合代码与testcase包里的测试文件名研究

    二,pagehelp目录

    这里写图片描述

    下面是ContactsHelper的代码

    public class ContactsHelper {
    
        public static Logger logger=Logger.getLogger(ContactsHelper.class);
        /**
         * @author tangjun
         * @param appiumUtil Appium封装对象引用
         * @param byElement 要点击的元素By对象
         * @description 在首页上进行点击操作
         * */
        public static  void clickOnPage(AppiumUtil appiumUtil,By byElement){
            appiumUtil.click(byElement);
    
    
        }
    
        /**
         * 输入内容,一般用在edittext控件
         * @param appiumUtil
         * @param byElement
         * @param str
         */
        public static void typeInfo(AppiumUtil appiumUtil,By byElement,String str){
            appiumUtil.typeContent(byElement, str);
    
    
        }
    
    }

    代码很简单,就是一个点击,一个输入文本,两个方法都接受By 类型作为参数,那这个自然就指向pages目录里的文件了,看下一个目录

    三,pages目录

    这里写图片描述

    这是Contacts的代码

    public class Contacts {
    
        public static final By ADD_CONTACTS = By.id("com.example.android.contactmanager:id/addContactButton");//第一页添加联系人按钮
        public static final By CONTACT_NAME = By.id("com.example.android.contactmanager:id/contactNameEditText");//第二页contact name输入框
        public static final By CONTACT_PHONE = By.id("com.example.android.contactmanager:id/contactPhoneEditText");//第二页contact phone输入框
        public static final By CONTACT_EMAIL = By.id("com.example.android.contactmanager:id/contactEmailEditText");//第二页contact email输入框
        public static final By CONTACT_PHONE_SPINNER = By.id("com.example.android.contactmanager:id/contactPhoneTypeSpinner");//第二页住宅 家,选择器
        public static final By CONTACT_SPINNER_PHONE = By.xpath("//android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.LinearLayout[2]/android.widget.ListView[1]/android.widget.CheckedTextView[3]");//第二页号码类型是手机号
    
    
        public static final By SAVE = By.id("com.example.android.contactmanager:id/contactSaveButton");//save按钮
    
    }

    不多解释,很简单的从页面抓取元素的方法,这些ID或XPath的值在上一篇中有提及,使用uiautomatorViewer可快速获取到

    四,testcase目录

    这里写图片描述

    Contacts_001_addcontact_Test代码:

    public class Contacts_001_addcontact_Test extends BasePrepare {
        @Test
        public void login() {
    
            appiumUtil.time(3000);
            ContactsHelper.clickOnPage(appiumUtil, Contacts.ADD_CONTACTS);
            appiumUtil.time(3000);
            ContactsHelper.typeInfo(appiumUtil, Contacts.CONTACT_NAME,"楚乔");
            appiumUtil.time(2000);
            ContactsHelper.typeInfo(appiumUtil, Contacts.CONTACT_PHONE,"18900000009");
            appiumUtil.time(2000);
            ContactsHelper.clickOnPage(appiumUtil, Contacts.CONTACT_PHONE_SPINNER);
            appiumUtil.time(2000);
            ContactsHelper.clickOnPage(appiumUtil, Contacts.CONTACT_SPINNER_PHONE);
            appiumUtil.time(2000);
            ContactsHelper.typeInfo(appiumUtil, Contacts.CONTACT_EMAIL,"xiner@chuqiaozhuan.com");
            appiumUtil.time(3000);
            ContactsHelper.clickOnPage(appiumUtil, Contacts.SAVE);
    
    
        }
    }

    这就是一个完整的测试流程代码,这也是这个目录为什么叫testcase的原因,代码中有很多段这样的代码appiumUtil.time(2000),这个方法就是让线程睡眠一定时间让UI操作有一定的时间间隔,而不至于因为线程执行太快导致页面操作流程无法这么迅速地完成,上面代码很多次提及appiumUtil类,而到现在我们还没有看到这个类的代码,我们把这个类放在最后一个目录中,见下一个目录。

    五,utils目录

    这里写图片描述

    AppiumUtil文件代码:

    public class AppiumUtil {
    
        public static AppiumDriver<WebElement> driver;
        public ITestResult it;
        /** 定义日志输出对象 */
        public static Logger logger = Logger.getLogger(AppiumUtil.class);
    
        /**
         * 获取driver
         * 
         * @throws MalformedURLException
         * @throws
         */
        public AppiumDriver<WebElement> getDriver(String url,
                DesiredCapabilities cap) throws MalformedURLException {
            driver = new AndroidDriver<WebElement>(new URL(url), cap);
    
            return driver;
    
        }
    
        public AppiumDriver<WebElement> selectDriver(AppiumUtil appiumUtil,
                String url, DesiredCapabilities cap) throws MalformedURLException {
            driver = appiumUtil.getDriver(url, cap);
            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<WebElement> 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 byFindElement(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("com.yd.android.ydz:id/"
                        + 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<Boolean>() {
    
                            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 + "]");
    
        }
    
        // 时间
        public static void time(int t) {
            try {
                Thread.sleep(t);
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        /* 向上滑动10次 */
        public static void scorllUp() {
            int x = driver.manage().window().getSize().width;
            int y = driver.manage().window().getSize().height;
            int during;
            try {
                for (int i = 1; i < 10; i++) {
                    Thread.sleep(3000);
                    driver.swipe(x / 2, y * 9 / 10, x / 2, y / 10, 500);
                    Thread.sleep(3000);
                }
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        // 只滑动一次
        public void scorllUp1() {
            int x = driver.manage().window().getSize().width;
            int y = driver.manage().window().getSize().height;
            int during;
            try {
    
                Thread.sleep(1000);
                driver.swipe(x / 2, y * 9 / 10, x / 2, y / 10, 1000);
                Thread.sleep(1000);
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        // 向上滑动一次
        public void scorllUp4() {
            int x = driver.manage().window().getSize().width;
            int y = driver.manage().window().getSize().height;
            int during;
            try {
    
                Thread.sleep(1000);
                driver.swipe(x / 2, y / 10, x / 2, y * 9 / 10, 1000);
                Thread.sleep(1000);
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        public void scorllUp2() {
            int x = driver.manage().window().getSize().width;
            int y = driver.manage().window().getSize().height;
            int during;
            try {
    
                Thread.sleep(3000);
                driver.swipe(x / 2, y / 10, x / 2, y * 9 / 10, 500);
                Thread.sleep(3000);
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        /**
         * 向左滑动
         */
        public void scorllUp3() {
            int x = driver.manage().window().getSize().width;
            int y = driver.manage().window().getSize().height;
            int during;
            try {
    
                driver.swipe(x * 9 / 10, y / 2, x / 10, y / 2, 2000);
    
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
        // 判断关键字是否存在
        public static boolean getPageSouce(String aaa) {
            return driver.getPageSource().contains(aaa);
        }
    
        // 关键字封装
        public static WebElement byId(AppiumDriver driver, String aaa) {
            return byId(driver, aaa, "", "");
        }
    
        /**
         * 
         * @param driver
         * @param aaa
         *            ("com.yd.android.ydz:id/" + )
         * @param bbb
         *            ("com.yd.android.camera:id/" + )
         * @return
         */
        public static WebElement byId(AppiumDriver driver, String aaa, String bbb,
                String ccc) {
            By by = null;
            if (StringUtils.isNotEmpty(aaa)) {
                by = By.id("com.yd.android.ydz:id/" + aaa);
            } else if (StringUtils.isNotEmpty(bbb)) {
                // 小米
                by = By.id("com.android.camera:id/" + bbb);
            } else if (StringUtils.isNotEmpty(ccc)) {
                // 魅族手机
                by = by.id("com.meizu.media.camera:id/" + ccc);
            } else {
                logger.info("参数错误无法初始化。。。");
            }
            return driver.findElement(by);
        }
    
        /**
         * 暂停当前用例的执行,暂停的时间为: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<String> 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<String> 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 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);
        }
    
        public static void coorDinate(AppiumDriver driver, int x, int y,
                int duration) {
            JavascriptExecutor js = (JavascriptExecutor) driver;
            HashMap<String, Integer> tapObject = new HashMap<String, Integer>();
            tapObject.put("x", x);
            tapObject.put("y", y);
            tapObject.put("duration", duration);
            js.executeScript("mobile: tap", tapObject);
        }
    
        /**
         * 获取随机数
         */
        public static int getNum(int start, int end) {
            return (int) (Math.random() * end + start);
        }
    
        /**
         * 获取随机英文字母
         */
        private static char testZimu() {
            String chars = "abcdefghijklmnopqrstuvwxyz";
            return chars.charAt((int) (Math.random() * 26));
        }
    
        /**
         * seekbar拖动
         * 
         * @param appiumUtil
         */
        public static void seekBar(AppiumUtil appiumUtil) {
            WebElement Slider = driver.findElement(By
                    .id("com.aldb.android:id/loan_total_seekbar"));
            int start = Slider.getLocation().getX();
            int end = start + Slider.getSize().getWidth();
            int y = Slider.getLocation().getY();
    
            TouchAction act = new TouchAction(driver);
            act.press(start, y).waitAction(800).moveTo(end - 1, y).release()
                    .perform();
    
        }
    
        /**
         * 切换到webview界面
         */
        public static void handle() {
            Set<String> contexts = driver.getContextHandles();
    
            for (String context : contexts) {
                logger.info(context);
    
            }
    
            driver.context((String) contexts.toArray()[1]);
    
        }
    
        /**
         * 切换到app端
         */
        public static void handles() {
            Set<String> contexts = driver.getContextHandles();
            for (String context : contexts) {
                logger.info(context);
    
            }
            driver.context((String) contexts.toArray()[0]);
        }
    
        public static void wait1() {
            try {
                final WebDriver wait = (WebDriver) new WebDriverWait(driver, 10);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
    
        }
    
    }

    代码注释的很清楚,可以大略了解一下,封装了一下大部分页面操作会用到的方法,主要的几个就是点击,输入,滑动等等几个操作,然后我们先看SelectDriver的代码

    public class SelectDriver {
            //声明driver
            public  AppiumDriver<WebElement> driver; 
            //声明DesiredCapabilities
    
            //声明ITestContext,用于获取testng配置文件内容
            public ITestContext testContext;
            //appium server地址
            public String serverURL;
            //测试引擎名字
            public String automationName;
            //测试平台名字
            public String platformName;
            //测试平台版本号
            public String platformVersion;
            //设备名字
            public String deviceName;
    
            //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 String udid;
            //实例化本类的日志输出对象
            public static Logger logger = Logger.getLogger(SelectDriver.class);
    
            public  AppiumDriver<WebElement> selectDriver(ITestContext context,AppiumUtil appiumUtil) throws MalformedURLException{
                  //通过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文件获取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文件获取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");
    
                  sessionOverride = Boolean.valueOf(context.getCurrentXmlTest().getParameter("sessionOverride"));
                  udid=context.getCurrentXmlTest().getParameter("udid");
    
                  this.testContext = context;
                  DesiredCapabilities cap  = new DesiredCapabilities();
                  //告诉测试程序,当前项目目录在哪里
    
                  //设置capability,以便和appium创建session
                  cap.setCapability("platformName",platformName);
                  cap.setCapability("platformVersion",platformVersion);
                  cap.setCapability("androidAppPath", androidAppPath);
                  cap.setCapability("deviceName",deviceName);
                  cap.setCapability("sessionOverride", sessionOverride);
                  cap.setCapability("udid", udid);
    
    
                    cap.setCapability("unicodeKeyboard", unicodeKeyboard);
                    cap.setCapability("resetKeyboard", resetKeyboard);
                    cap.setCapability("automationName",automationName);
                    cap.setCapability("appPackage", appPackage);
                    cap.setCapability("appActivity", appActivity);
                      driver = appiumUtil.getDriver(serverURL, cap);
                      testContext.setAttribute("APPIUM_DRIVER", driver);
    
                      logger.info(PropertiesDataProvider.getTestData(appFilePath, appPackage)+"已经启动");
    
                      driver.manage().timeouts().implicitlyWait(elementTimeOut, TimeUnit.SECONDS);
                      return driver;
    
            }
    
    
    
        }

    从xml 的配置文件中取得所需配置参数,使用appiumUtil类构建出driver供BasePrepare调用,这个配置文件根据maven的运行方法,放在最外层的目录,命名为testng.xml代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="魔法现金" parallel="tests" thread-count="1">
    
        <!--server地址 -->
        <parameter name="serverURL" value="http://127.0.0.1:4723/wd/hub" />
        <!--automationName为selendroid or appium,如果sdk版本>=17请使用appium;如果sdk版本<=17请使用selendroid -->
        <parameter name="automationName" value="Appium" />
        <!-- 测试平台 iOS和Android -->
        <parameter name="platformName" value="Android" />
        <!-- 平台版本 -->
        <parameter name="platformVersion" value="4.4" />
        <!-- 设备名字,可随意起名字,但是要有意义 -->
        <parameter name="deviceName" value="xiaomi" />
        <!-- android app路径 -->
        <parameter name="androidAppPath" value="res/app/android/ContactManager.apk" />
    
        <!--app的包 -->
        <parameter name="appPackage" value="com.example.android.contactmanager" />
        <!--app的 activity -->
        <parameter name="appActivity" value=".ContactManager" />
        <!--是否支持unicode输入设置为true可以输入中文字符 -->
        <parameter name="unicodeKeyboard" value="true" />
        <!-- 重置键盘输入法 -->
        <parameter name="resetKeyboard" value="true" />
        <!--设备UDID iPhone真机使用或者android并行测试可以使用 -->
        <parameter name="udid" value="3DN6T16928001972" />
        <!-- 设置为true之后会覆盖当前session -->
        <parameter name="sessionOverride" value="true" />
        <!-- 进程等待1秒中的控制时间,单位是秒 -->
        <parameter name="sleepTime" value="1" />
        <!-- 页面元素15秒不出现超时时间 -->
        <parameter name="elementTimeOut" value="15" />
        <!-- app属性文件 -->
        <parameter name="appFilePath" value="res/properties/app.properties" />
    
           <test name="添加通讯录模块" preserve-order="true"> 
           <!--  <packages>
                <package name="包目录" />
                </packages>  -->
                <classes>
                    <class name="Contacts_001_addcontact_Test所在包目录" />
                    </classes> 
            </test>
    
    </suite> <!-- Suite -->

    这个便是程序的执行入口,遵循maven的项目管理方式,将utils包中的几个类补充一下:

    ExcelDataProvider的代码:主要用途是从Excel表读取数据

    /**
     * @author tangjun
     * @description: 读取Excel数据<br>
     *               说明:<br>
     *               Excel放在Data文件夹下<br>
     *               Excel命名方式:测试类名.xls<br>
     *               Excel的sheet命名方式:测试方法名<br>
     *               Excel第一行为Map键值<br>
     */
    public class ExcelDataProvider implements Iterator<Object[]> {
    
        private Workbook book = null;
        private Sheet sheet = null;
        private int rowNum = 0;
        private int currentRowNo = 0;
        private int columnNum = 0;
        private String[] columnnName;
        private String path = null;
        private InputStream inputStream = null;
        public static Logger logger = Logger.getLogger(ExcelDataProvider.class.getName());
    
        /* 
         * @description 
         * 2个参数:<br>
         * moduleName - 模块的名称
         * caseNum - 测试用例编号
         **/
        public ExcelDataProvider(String moduleName, String caseNum) {
    
            try {
                //文件路径
                path = "data/"+moduleName+".xls";
    
                 inputStream = new FileInputStream(path);
    
                book = Workbook.getWorkbook(inputStream);
                // sheet = book.getSheet(methodname);
                sheet = book.getSheet(caseNum); // 读取第一个sheet
                rowNum = sheet.getRows(); // 获得该sheet的 所有行
                Cell[] cell = sheet.getRow(0);// 获得第一行的所有单元格
                columnNum = cell.length; // 单元格的个数 值 赋给 列数
                columnnName = new String[cell.length];// 开辟 列名的大小
    
                for (int i = 0; i < cell.length; i++) {
                    columnnName[i] = cell[i].getContents().toString(); // 第一行的值
                                                                        // 被赋予为列名
                }
                this.currentRowNo++;
    
            } catch (FileNotFoundException e) {
                logger.error("没有找到指定的文件:" + "[" + path + "]");
                Assert.fail("没有找到指定的文件:" + "[" + path + "]");
            } catch (Exception e) {
                logger.error("不能读取文件: [" + path + "]",e);
                Assert.fail("不能读取文件: [" + path + "]");
            }
        }
        /**是否还有下个内容*/
    
        public boolean hasNext() {
    
            if (this.rowNum == 0 || this.currentRowNo >= this.rowNum) {
    
                try {
                    inputStream.close();
                    book.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return false;
            } else {
                // sheet下一行内容为空判定结束
                if ((sheet.getRow(currentRowNo))[0].getContents().equals(""))
                    return false;
                return true;
            }
        }
        /**返回内容*/
        public Object[] next() {
    
            Cell[] c = sheet.getRow(this.currentRowNo);
    
            Map<String, String> data = new HashMap<String, String>();
    
            for (int i = 0; i < this.columnNum; i++) {
    
                String temp = "";
    
                try {
                    temp = c[i].getContents().toString();
                } catch (ArrayIndexOutOfBoundsException ex) {
                    temp = "";
                }
    
                data.put(this.columnnName[i], temp);
            }
            Object object[] = new Object[1];
            object[0] = data;
            this.currentRowNo++;
            return object;
        }
    
        public void remove() {
            throw new UnsupportedOperationException("remove unsupported.");
        }
    
    
    }

    LogConfiguration的代码:主要用途是生成每条用例的日志

     /* @decription 动态生成各个模块中的每条用例的日志,运行完成用例之后请到result/log目录下查看
     * */
    public class LogConfiguration {
    
            public static void initLog(String fileName){
                //获取到模块名字
                String founctionName = getFunctionName(fileName);
                //声明日志文件存储路径以及文件名、格式
                final String logFilePath  = "./result/log/"+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", "./result/log/"+founctionName+"/"+fileName+".log");
                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, fileName.indexOf("_"));
                return functionName;
    
        }
    
    
    }

    PropertiesDataProvider的代码,主要用来从.properties文件中读取相关测试数据

    * @Desription 从.properties文件中读取相关测试数据<br>
     * 
     * */
    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));
    
        }
    }

    这么多文件看起来相当的混乱,所以我把这个demo上传到了百度云,地址是链接: https://pan.baidu.com/s/1jIqTP4a 密码: sm36,还有在这补充一点的是项目是用了Intelli IDEA工具,并不是上一篇中直接在Android项目中构造一个JavaLibrary,很多地方也只是贴了代码并未详细描述,建议下载demo运行测试学习,谢谢

    展开全文
  • 不过宏哥经过一段时间的准备,appium的自动化测试框架完善的差不多了,那么接下来宏哥继续给小伙伴和童鞋们分享有关Appium自动化测试框架综合实践。想必小伙伴们有点等不及了吧!driver配置封装kyb_caps.yaml 配置表...
  • 在将近三年的移动端测试工作生涯的积累和学习过程中,趁着在过年的假期时间得空,决定出一系列appium自动化测试框架的学习经历分享给各位有兴趣的同学们。  言归正传,首先在正式开始之前让我们先简单介绍一下该...
  • python+unnitest+appium自动化测试框架: 以下是本篇文章正文内容,下面案例可供参考 一、python是什么? Python由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计,作为一门叫做ABC语言的替代...
  • 介绍在Windows 10 环境上,搭建 Appium 自动化测试框架。 首先系统需要一些开发环境: Python\JDK\Andriod SDK 这三个是Android测试的一些基本环境配置,如果不会,参见我的其他博客。 具备上面的这些基本环境后,...
  • Java+Appium自动化测试框架(二) 定位方式发布时间:2020-04-06 16:18:03来源:51CTO阅读:1729作者:木子耳07package com.appium.test;/*** @author YuFeifei* @version 2017年11月15日 上午11:41:21* 类说明* 根据...
  • 基于python单元测试框架unittest完成appium自动化测试,生成基于html可视化测试报告代码示例:1 #利用unittest并生成测试报告2 class Appium_test(unittest.TestCase):3 """appium测试类"""4 def setUp(self):5 ...
  • 本篇章紧接上一篇继续来分享关于 Appium 自动化测试框架综合实践案例代码。框架所需要的代码实现都已基本完成。 data数据封装 1.使用背景 在实际项目过程中,我们的数据可能是存储在一个数据文件中,如txt,excel、...
  • 最接近模拟手工操作的,是UI自动化测试。虽然不如接口测试那么稳定,不如单元测试那么精准。但也能解放下双手,提高效率。对测试人员来说,很多回归测试,并不能发现啥问题,但不去测,又不放心。万一出现bug了呢?...
  • 基于python单元测试框架unittest完成appium自动化测试,生成基于html可视化测试报告代码示例:#利用unittest并生成测试报告class Appium_test(unittest.TestCase):"""appium测试类"""def setUp(self):desired_caps =...
  • appium自动化测试框架构建

    万次阅读 多人点赞 2014-04-07 15:08:07
    Appium是一个开源、跨平台的测试框架,可以用来测试原生及混合的移动端应用。Appium支持IOS、Android及FirefoxOS平台。Appium使用WebDriver的json wire协议,来驱动Apple系统的UIAutomation库、Android系统的...
  • 不过宏哥经过一段时间的准备,appium的自动化测试框架完善的差不多了,那么接下来宏哥继续给小伙伴和童鞋们分享有关Appium自动化测试框架综合实践。想必小伙伴们有点等不及了吧! driver配置封装 kyb_caps.yaml ...
  • Android + Appium 自动化测试框架

    千次阅读 2019-07-26 17:23:01
    Appium测试框架搭建 本文代码github地址:https://github.com/yueyue10/MyApplication/tree/master/python_project/python_demo/appium_demo 因为Appium网上的资料大多都是基础的,包括环境搭建、单界面的测试、单...
  • 那么面对市面上林林总总的自动化测试框架和工具,为什么说Appium自动化测试框架的统治级优势呢,下面先看一下各大主流框架的对比及优势。  一、主流框架对比  下面对比了市面上主流的几大框架:  二、各大框架...
  • Appium简介 Appium是一个开源、跨平台的测试框架,可以用来测试原生及混合的移动端应用。Appium支持iOS、Android及FirefoxOS平台测试。Appium使用WebDriver的json wire协议,来... 相比其他的移动自动化测试工具...
  • Appium是开源、跨平台的测试框架,可以用来测试原生及混合的移动端应用。Appium支持IOS、Android及FirefoxOS平台测试。使用Webdriver的json wire协议,来驱动Apple的UIAutomation库、Android的UIAutomator框架。 ...
  • 1.在utils包中创建一个AppiumUtil类,这个类是对appium api进行封装的。 代码如下: package utils; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import java.util....
  • 大家好,我是金主,本章主要讲 python 简单脚本编写,非常适合小白学习,入门必备,废话不多说,直奔主题本章主要讲:python 实现一个登陆的自动化脚本 # -*-coding:utf8-*-from time import sleepfrom appium ...
  • appium自动化测试框架中,需要的android开发环境,一共两种:jdk、sdk。搭建攻略: 1.安装JDK 根据操作系统情况可在Oracle官网免费下载所需版本的JDK,不过官网下载需要先注册一个账号。网址:...
  • Appium测试框架搭建 本文代码github地址:https://github.com/… 因为Appium网上的资料大多都是基础的,包括环境搭建、单界面的测试、单控件的测试等。 关于Appium测试框架的文章却没有查到,所以这篇文章主要是关于...
  • public void setUp() throws Exception { // set up appium File classpathRoot = new File(System.getProperty("user.dir")); File appDir = new File(classpathRoot, "../../../apps/ContactManager/"); File app ...
  • 最近晚上都在找APP做UI自动化测试,主要是学习设计流程,权当温习。今晚练习了一个小项目,就是手机自带的“计算器”,练习加减乘除的运算。第一步:新建一个文件夹,练习的项目都放在这个文件夹内。(pycharm上新建)...
  •  今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现。到今天为止,大功即将告成;框架所需要的代码实现都基本完成。 2.data数据封装 2.1使用背景 在实际项目过程中,我们的数据可能是存储在...
  • #coding=utf-8#1.先设置编码,utf-8可支持...'''Created on 2019-11-13@author: 北京-宏哥 QQ交流群:707699217Project:Appium自动化测试框架综合实践 - 代码实现'''#3.导入模块from kyb_testProject.baseView.base...
  • appium是一个开源的移动端自动化测试框架,可以测试原生的、混合的以及移动端的web项目,appium是跨平台的,可以运行在osx、windows以及linux桌面系统上,用来测试ios、android应用 Selenium 2004年,Thoughtworks...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,514
精华内容 605
关键字:

appium自动化测试框架