通过之前方式也能进行测试脚本的实现,但效率不高,测试框架的作用就是为了提高后续脚本的编写效率而进行的一系列的抽取、封装、优化等操作。
现在可以新建一个包,比如较common,用来存放一些通用的类,然后在这个包下新建一个类Helper,在这个列里对常用的定位方式和操作进行封装,这样后续进行控件定位或者操作就会比较方便,不用卸太长的代码,后续如果要对某个操作的方法进行改动的话也只需要来这个类对应的方法进行修改而无需去调用到的地方处处修改。封装完后的测试代码如下(每个方法是干嘛的都有添加对应的注释):
package appium.common;
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.Set;
public class Helper {
private AppiumDriver driver;
//设置默认超时时间
private final long DEFAULT_TIMEOUT = 5;
private final long LONG_TIMEOUT = 10;
//通过构造方法来初始化AppiumDriver对象引用,这样才能获得Appium提供的方法
public Helper(AppiumDriver driver){
this.driver = driver;
}
//封装智能等待的方法,取代Thread.sleep(),这样可以节省时间,默认等待5秒
public void waitForElement(By by){
WebDriverWait wait = new WebDriverWait(driver,DEFAULT_TIMEOUT);
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
}
//如果需要等待比较长的时候可以调用这个方法
public void waitForElementForLongTime(By by){
WebDriverWait wait = new WebDriverWait(driver,LONG_TIMEOUT);
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
}
/**
* 封装常见定位方法
*/
//按id定位
public WebElement findById(String id){
waitForElement(By.id(id));
return driver.findElement(By.id(id));
}
//多个相同id的定位方法,index从0开始
public WebElement findById(String id, int index){
waitForElement(By.id(id));
return (WebElement) driver.findElements(By.id(id)).get(index - 1);
}
//按控件类型定位
public WebElement findByClassName(String clsName,int index){
waitForElement(By.className(clsName));
return (WebElement) driver.findElements(By.className(clsName)).get(index - 1);
}
//按文本定位
public WebElement findByText(String text){
waitForElement(By.xpath("//*[@text='"+text+"']"));
return driver.findElement(By.xpath("//*[@text='"+text+"']"));
}
//多个相同文本,可通过这个方法去获取
public WebElement findByText(String text,int index){
waitForElement(By.xpath("//*[@text='"+text+"']"));
return (WebElement) driver.findElements(By.xpath("//*[@text='"+text+"']")).get(index - 1);
}
//常见控件的定位方式
//控件点击
public void click(WebElement element){
System.out.println("执行控件点击操作--->");
element.click();
}
//控件输入
public void enterText(WebElement element,String text){
System.out.println("执行输入操作,输入文本为"+text+"--->");
element.sendKeys(text);
}
public void clearText(WebElement element){
System.out.println("执行清空输入框操作--->");
element.clear();
}
//清空
public void enterTextWithPreClear(WebElement element,String text){
clearText(element);
enterText(element,text);
}
//获取文本
public String getText(WebElement element){
System.out.println("执行如下操作:获取控件文本--->");
return element.getText();
}
//硬件返回
public void goBack(){
driver.navigate().back();
}
//webview相关操作的API
//通过xpath定位
public WebElement findByXpath(String xpath){
return driver.findElementByXPath(xpath);
}
//获取当前的句柄集合
public Set<String> getContentHandlers(){
return driver.getContextHandles();
}
//切换执行环境
public void content(String name){
driver.context(name);
}
}
这个类主要封装了常见的定位方式,然后在每种定位方式里都添加了智能等待的方法,然后封装了一些常见API以及webview相关操作的方法,后续所有的操作方法都从这个类获取,后续需要改动的时候只需要在这个类里对具体的方法实现进行改动即可。
POP是Page Object Pattern的缩写,就翻译成面向页面的模式吧,它的大概思想是将控件的操作按页面进行划分,每个页面所涉及到的操作可以封装到页面类里,然后脚本需要用到的操作都从各个Page类去获取,这样有利于后期脚本的维护,比如某个页面的某些控件的定位方式变了这时候只需要去修改这个控件所属的Page类对应的获取该控件的方法实现就行,而调用到这个控件的各个脚本都无需修改了。如果Page类抽取得完善的话,具体脚本实现的人就相当于从各个Page类里挑出所需的方法进行拼装得到具体的脚本即可。
现在可以新建一个包,如pages,用来存放各个Page类。
有些操作可能被很多个页面都使用到,这时候可以新建一个公共的Page类,对这些共有的操作进行简要的封装,如常见的导航操作,新建CommonPage,每个Page类都有类似的结构,一个构造方法用来给Helper类实例化,这样就可以引入我们上一小节封装的所有 方法,接着就封装会用到的控件以及控件操作,完成的测试代码如下所示:
package appium.pages;
import appium.common.Helper;
import org.openqa.selenium.WebElement;
public class PageCommon {
private Helper helper;
//通过构造方法给Helper赋值,这样就可以使用Helper方法的方法
public PageCommon(Helper helper){
this.helper = helper;
}
//封装底部的各个tab页,通过uiautomatorviewer查看控件id
public WebElement getHomeTab(){
return helper.findById("net.oschina.app:id/nav_item_news");
}
public WebElement getMomentTab(){
return helper.findById("net.oschina.app:id/nav_item_tweet");
}
public WebElement getPublishBtn(){
return helper.findById("net.oschina.app:id/nav_item_tweet_pub");
}
public WebElement getExploreTab(){
return helper.findById("net.oschina.app:id/nav_item_explore");
}
public WebElement getMySettingsTab(){
return helper.findById("net.oschina.app:id/nav_item_me");
}
//封装点击各个tab页的方法
public void goToHomeTab(){
helper.click(getHomeTab());
}
public void goToMomentTab(){
helper.click(getMomentTab());
}
public void clickPublishBtn(){
helper.click(getPublishBtn());
}
public void goToExploreTab(){
helper.click(getExploreTab());
}
public void goToMySettingsTab(){
helper.click(getMySettingsTab());
}
//封装硬件返回的方法
public void goBack(){
helper.goBack();
}
}
在这个类里主要封装了底部导航栏的操作,后续如果有其他各个Page类都可能会使用到的操作都可以加到这个Page类里去实现。
后续会介绍到具体的脚本实现会使用的页面有我的设置页面,登录页面可以对这两个页面常见的控件和操作进行封装,控件的定位信息一样是通过uiautomatorviewer工具获取到,抽取两个Page类具体如下测试代码所示:
我的设置页面的常用封装如下:
package appium.pages;
import appium.common.Helper;
import org.openqa.selenium.WebElement;
public class PageMySettings {
private Helper helper;
public PageMySettings(Helper helper){
this.helper = helper;
}
//抽取顶部设置按钮项
public WebElement getSetingsBtn(){
return helper.findById("net.oschina.app:id/iv_logo_setting");
}
//抽取登录按钮项
public WebElement getLoginIcon(){
return helper.findById("net.oschina.app:id/iv_portrait");
}
//抽取我的消息列表项
public WebElement getMyMsgItem(){
return helper.findById("net.oschina.app:id/rl_message");
}
//抽取我的博客列表项
public WebElement getMyBlogItem(){
return helper.findById("net.oschina.app:id/rl_blog");
}
//抽取我的活动列表项
public WebElement getMyActivityItem(){
return helper.findById("net.oschina.app:id/rl_info_avtivities");
}
//抽取我的团队列表项
public WebElement getMyTeamItem(){
return helper.findById("net.oschina.app:id/rl_team");
}
//获取昵称控件
public WebElement getNickNameTextView(){
return helper.findById("net.oschina.app:id/tv_nick");
}
//后期收藏布局
public WebElement getFavoritesLayout(){
return helper.findById("net.oschina.app:id/ly_favorite");
}
//封装各个点击方法
public void clickMyMsgItem(){
helper.click(getMyMsgItem());
}
public void clickMyBlogItem(){
helper.click(getMyBlogItem());
}
public void clickMyActivityItem(){
helper.click(getMyActivityItem());
}
public void clickMyTeamItem(){
helper.click(getMyTeamItem());
}
public void clickLoginIcon(){
helper.click(getLoginIcon());
}
public void clickSettingsBtn(){
helper.click(getSetingsBtn());
}
public void clickFavoritesLayout(){
helper.click(getFavoritesLayout());
}
//封装获取昵称文本的方法
public String getNickName(){
return helper.getText(getNickNameTextView());
}
}
登录界面常见的操作封装如下:
package appium.pages;
import appium.common.Helper;
import org.openqa.selenium.WebElement;
public class PageLogin {
private Helper helper;
public PageLogin(Helper helper){
this.helper = helper;
}
//获取用户名输入框
public WebElement getName(){
return helper.findById("net.oschina.app:id/et_username");
}
//获取密码输入框
public WebElement getPwd(){
return helper.findById("net.oschina.app:id/et_password");
}
//获取登录按钮
public WebElement getLoginBtn(){
return helper.findById("net.oschina.app:id/btn_login");
}
//清空用户名输入框
public void clearName(){
helper.clearText(getName());
}
//清空密码输入框
public void clearPwd(){
helper.clearText(getPwd());
}
//输入用户名
public void enterName(String name){
helper.enterText(getName(),name);
}
//输入密码
public void enterPwd(String password){
helper.enterText(getPwd(),password);
}
//点击登录按钮
public void clickLoginBtn(){
helper.click(getLoginBtn());
}
//封装登录的方法,只需要提供用户名和密码
public void login(String name,String password){
clearName();
enterName(name);
clearPwd();
enterPwd(password);
clickLoginBtn();
}
//判断是否登录界面打开的方法
public boolean isLoginScreenOpened(){
boolean result = false;
if(getLoginBtn() != null){
return getLoginBtn().isDisplayed();
}
return result;
}
}
至此我们已经介绍了三个Page类的抽取,后续 其他Page类的抽取就跟这些类似了。