UI层自动化测试框架(四):对象库层

引言

本章主要介绍自动化测试框架–对象库层。该层是UI自动化中比较关键的一层,设计自动化框架,不可避免的就是对象库,有一个好的对象库,可以让整个测试框架可维护性更高,大大增强了代码的复用性。

讲之前先和大家普及个一概念:PO模式

PO模式

那什么叫PO模式,为什么要用PO模式?引用如下一段话,你就会恍然大悟~

PO模式,全称Page Object模式,是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在写测试脚本时,可以通过调用页面类来获取页面元素。当页面某个元素id或者位置变化时,这时不用更改测试脚本,只用改下对应的页面类就行了。

上面这段话,总结一下就是:PO就是一个设计模式,将代码以页面为单位进行组织,针对这个页面上的所有信息,相关操作都放到一个类中;从而使具体的测试用例变成了简单的调用和验证操作。

如果你深刻理解了就应该知道了PO对象的好处,当然这只是阐述啦它的可维护性,但它的复用性就更好理解,这里就不多做解释。

对象库的引入

对于上面讲的PO模式,大家会不会有一个疑惑?这样一来一个app有很多page,以DJI GO为例50个左右肯定是有的吧,那这样是不是要设计50个页面类,然后每个页面类中写对应的元素。这样一来单单页面类就写这么多,感觉工程量太大,而且代码的复用性不高。

那有没有什么改进的办法呢?

1.首先定义一个BasePage类,毕竟所有的页面都有共同的东西,每个页面都有元素,每个页面元素都有相应的方法。该类包括一个成员变量 pageName,和封装的的方法。重写它的构造方法,用来初始化成员变量。以后每次调用某个页面时,只用new一个BasePage对象,传入对应的参数pageName,就获得对应页面。

这里写图片描述

package com.dji.object;

import java.io.IOException;
import java.util.HashMap;

import com.dji.utils.AppiumExecutorImpl;
import com.dji.utils.Log;
import com.dji.utils.XmlUtils;


import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;


/**
 * 封装一个BasePage的类,毕竟所有的页面都有共同的东西,每个页面都有元素,每个页面元素都有相应的方法
 * 
 * @author Charlie.chen
 *
 */

public class BasePage extends AppiumExecutorImpl {

    protected AppiumDriver<?> driver;
    protected String pageName;  //页面名称
    protected String xmlPath;   //页面元素路径
    protected HashMap<String, Locator> locatorMap;
    public Log log = new Log(this.getClass());


    public BasePage(AppiumDriver<?> driver,String pageName) throws Exception  {
        super(driver);
        this.driver = driver;
        this.pageName=pageName;

        //获取资源文件page.xml的路径
        //xmlPath=System.getProperty("user.dir")+"\\src\\main\\java\\com\\dji\\pageObject\\Page.xml";   
        xmlPath=BasePage.class.getClassLoader().getResource("page.xml").getPath();

        //locatorMap = XmlUtils.readXMLDocument(xmlPath, this.getClass().getSimpleName());
        locatorMap = XmlUtils.readXMLDocument(xmlPath, pageName);

    }



    public void type(String locatorName, String values) {
        super.type(getLocator(locatorName), values);
        log.info("type value is:  " + values);
    }


    public void click(String locatorName) {
        super.click(getLocator(locatorName));
        log.info("click: "+locatorName);
    }


    public String getText(String locatorName) {
        // TODO Auto-generated method stub
        return super.getText(getLocator(locatorName));
    }


    public MobileElement findElement(String locatorName) {
        // TODO Auto-generated method stub
        return super.findElement(getLocator(locatorName));
    }

    public boolean isElementDisplayed(String locatorName) {
        // TODO Auto-generated method stub
        return super.isElementDisplayed(getLocator(locatorName));
    }



    /**
     * 根据locatorName获取Locator
     * 
     * @author Charlie.chen
     * @param locatorName
     * @return
     * @throws IOException
     */
    public  Locator getLocator(String locatorName) {

        Locator locator =  null;

        if(locatorMap!=null)
        {
            locator = locatorMap.get(locatorName);
        }
        return locator;
    }

}

上述代码中有个一个集合locatorMap,主要存储的对应的pageName和Locator内容

2.接下来封装元素,每个元素都有相应的定位地址(xpath路径或css或id),等待时间,定位方式

package com.dji.object;

/**
 * 封装页面元素,每个元素都有相应的定位地址(xpath路径或css或id),等待时间,定位方式
 * 
 * @author Charlie.chen
 *
 */

public class Locator {

    private String address;  //定位地址
    private int waitSec;    //等待时间
    private ByType byType;  //定位方式


    /**
     * 定位类型枚举
     * @author Charlie.chen
     *
     */
     public enum ByType{
            by, xpath, linkText, id, name, className
        }


    public Locator() {}

    /**
     * Locator构造器,默认定位类型By.xpath
     * 
     * @author Charlie.chen
     * @param element
     */
    public Locator(String address) {
        this.address = address;
        this.waitSec = 3;
        this.byType = ByType.xpath;
    }

    public Locator(String address, int waitSec) {
        this.waitSec = waitSec;
        this.address = address;
        this.byType = ByType.xpath;
    }

    public Locator(String address, int waitSec, ByType byType) {
        this.waitSec = waitSec;
        this.address = address;
        this.byType = byType;
    }

    public String getAddress() {
        return address;
    }

    public int getWaitSec() {
        return waitSec;
    }

    public ByType getBy() {
        return byType;
    }

    public void setBy(ByType byType) {
        this.byType = byType;
    }

    public ByType getByType() {
        return byType;
    }


}

对象库的管理

针对上面的两个类BasePage和Locator,其实就是分别代表页面对象库和元素对象库。关于对象库的管理,就是将对象库中的数据,类似pageName和元素属性id,xpth等分离出来保存在page.xml文件中,这样做到了数据隔离的效果,维护性更高。

page.xml如下

<?xml version="1.0" encoding="UTF-8"?>

<map>
    <!--locator of page map info -->

    <page pageName="menuPage">
        <!--Locator lists -->
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'设备')]">设备</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'编辑器')]">编辑器</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'天空之城')]">天空之城</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'我')]"></locator>
    </page

    <page pageName="minePage">
        <!--Locator lists -->
        <locator type="id" timeOut="3" value="dji.pilot:id/icon_user">用户图像</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_user_name">用户名</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/v2_mine_store">DJI商城</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/v2_mine_academy">DJI论坛</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'礼品卡')]">礼品卡</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'上传队列')]">上传列表</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'消息')]">消息</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'我的收藏')]">我的收藏</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'更多')]">更多</locator>
        <locator type="xpth" timeOut="3" value="//android.widget.TextView[contains(@text,'设置')]">设置</locator>                       
    </page>


    <page pageName="loginPage">
        <!--Locator lists -->
        <locator type="id" timeOut="3" value="dji.pilot:id/edt_email">登录输入账号框</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/edt_password">登录输入密码框</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/btn_sign_ok">登录</locator>
    </page>


    <page pageName="settingPage">
        <!--Locator lists -->
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_back_button">返回</locator>

        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_cellular_switch">使用手机流量上传文件</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_develop_switch">开启USB调试</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_reset_guide_button">重置新手指引</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_clean_cache_button">清除数据缓存</locator>                   
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_language_change">多语言</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_privacy_button">隐私设置</locator>
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_rate_app_button">给我们评分</locator>           
        <locator type="id" timeOut="3" value="dji.pilot:id/mine_settings_sign_out_button">退出DJI账号</locator>

        <locator type="id" timeOut="3" value="android:id/button2">取消</locator>
        <locator type="id" timeOut="3" value="android:id/button1">确定</locator>

        <locator type="id" timeOut="3" value="dji.pilot:id/mine_user_protocol">DJIGO用户协议</locator>              
    </page>

</map>

分析一下page.xml中登录页,对应的pageName=“loginPage”,对应的元素名有“登录输入账号框”,“登录输入密码框”,“登录按钮”和对应的定位方式和等待时间。这样是不是一目了然,以后如果页面元素属性发生变化,只用改下以上配置文件即可。关于xml文件的读取,在第三章中有讲过,通过XmlUtils进行读取,将读取的信息保存在HashMap集合locatorMap中。

总结

总结一句话:就是将页面和元素封装在两个类中,然后将对应的数据抽离出来放在xml文件中管理。
对象库大大提高了测试框架的复用性和可维护性,在测试框架中起到核心作用,这里只是我的处理方式,相信还有更简洁更易度的方法,等待大家去挖掘。

下一章主要讲解 自动化测试框架(五):业务层和用例层,敬请期待!!

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值