Appium入门自动化测试(十三)
前言
各位小伙伴们,又和大家见面了,今天我们聊一聊PO(Page Object Model)模式。PO模式是来自Selenium的一种测试分层的设计方式,可以把测试页面和页面元素分离,增加代码可重用行,能够让你的测试框架更容易维护,更方便管理。而Appium本质上还是继承Selenium的PO模式,自然而然的可以使用。可能又很多朋友了解过PO模式,这里要提一下PageFactory的概念, PageFactory 实际上是PO模式的延伸,主要是用来初始化PO模式种的页面对象。阅读此文大约需要20分钟,想要学设计测试框架的不妨深入理解一下PO模式。这里需要对用python的同学表达一下歉意,本篇内容还未内能够实现用python写。
那么如何理解PO模式呢? 让我们先从官方文档看起。官方文档如下:
http://www.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern
PO模式是page object model的缩写,顾名思义, 是一种设计模式,实现脚本的page和真实的网站页面Map起来,一一对应起来。这样能测试框架更容易维护。 比如一个登陆页面,使用PO模式后,会创建一个BaiduPage的class,该class会定义输入框,登陆搜索按钮的WebElenent ,而针对相应的Element实现相应的方法,输入框是用来输入的,就需要创建输入关键字的方法,这样就和真实的页面一致,所以这样的设计理念就是PO模式。 而PageFactory隶属PO模式,是用来初始化每个PO模式实现的Page Class,初始化对象库。
Page Object model 是当前比较流行的自动化设计模式.
用下面的图表示一下,更直观些:
如何使用PO
首先针对你的APP或Web,每一个页面都创建一个Class,把页面的元素和页面的控件一一对应起来,最好先创建一个BasePage的基类,封装一些比较常用的方法,让每个页面都继承该基类,然后对每个元素(页面控件)创建操作方法,比如输入框,要创建输入和清除以及获取该输入框内容的方法;比如是一个按钮,要创建点击该按钮和判断该按钮状态的方法。之后可以创建一个中间层,根据不同测试场景对之前的方法进行调用,比如搜索
a. 在输入框输入关键字
b. 点击搜索按钮
c. 获取搜索结果
在测试场景中可以把这一系列操作封装一个静态方法直接调用,在以后的每个case都能直接调用该方法进行测试。有朝一日百度主页有变化,无论如何关键字输入框和搜索按钮都在,这样你的所有用到搜索的用例无须修改,只需要在对应Page里面把定位方式或定位值修改一下。
如果使用PageFactory,需要在定义的Element添加@FindBy注解。也可以自己封装一套元素管理,比如把元素定位的方法写到xml中,以方便统一管理。
注:
在这里使用Web为例并非web比APP简单,而是我这里暂时没有iOS App和Android App界面一样的app。 原理一样,感兴趣可以自己研究。
首先创建BaiduPage
package main.java.com.dbyl.appiumCore.page;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.support.FindBy;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
public class BaiduPage {
AppiumDriver<?> driver;
@FindBy(xpath = "//input[@id='index-kw']")
MobileElement inputBox;
@FindBy(id = "index-bn")
MobileElement searchButton;
public BaiduPage(AppiumDriver<?> driver) {
super();
this.driver = driver;
if (driver instanceof AndroidDriver) {
System.out.println("Android driver");
} else if (driver instanceof IOSDriver) {
System.out.println("IOS driver");
}
}
/**
* @author young
* @param url
*/
public void open(String url) {
if (driver instanceof AndroidDriver) {
System.out.println("Android driver");
} else if (driver instanceof IOSDriver) {
System.out.println("IOS driver");
}
driver.get(url);
}
/**
* @author young
* @param keyword
*/
public void typeInputBox(String keyword) {
inputBox.sendKeys(keyword);
}
/**
* @author young
*/
public void clickOnSearchButton() {
searchButton.submit();
}
/**
* @author young
* @param timeout
*/
public void waitForPageLoad(int timeout) {
driver.manage().timeouts().pageLoadTimeout(timeout, TimeUnit.SECONDS);
}
/**
* @author young
* @return
*/
public String getTitle() {
return driver.getTitle();
}
}
接下来就可以根据Page 中封装的方法做一下搜索🔍这样的测试场景
创建中间层,使用Page
package main.java.com.dbyl.appiumCore.pageActions;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.openqa.selenium.support.PageFactory;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import main.java.com.dbyl.appiumCore.page.BaiduPage;
public class BaiduPageAction {
private static AppiumDriver<?> driver;
public static String url="http://www.baidu.com";
public static AppiumDriver<?> getDriver() {
return driver;
}
public static void setDriver(AppiumDriver<?> driver) {
BaiduPageAction.driver = driver;
}
/**
* @author young
* @param keyword
*/
public static void search(String keyword) {
BaiduPage baiduPage = new BaiduPage(driver);
PageFactory.initElements(new AppiumFieldDecorator(driver,60, TimeUnit.SECONDS), baiduPage);
baiduPage.open(url);
baiduPage.waitForPageLoad(60);
baiduPage.typeInputBox(keyword);
baiduPage.clickOnSearchButton();
baiduPage.waitForPageLoad(60);
Assert.assertTrue(baiduPage.getTitle().contains(keyword));
}
}
在测试用例中如何使用,直接使用上面的测试步骤即可
在测试用例中使用
package main.java.com.dbyl.appiumCore.tests
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.MobileCapabilityType;
import main.java.com.dbyl.appiumCore.pageActions.BaiduPageAction;
import main.java.com.dbyl.appiumServer.AppiumServerUtils;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.net.MalformedURLException;
import java.net.URL;
public class BaiduPageObjectDemo {
AppiumDriver<MobileElement> driver;
public URL url;
boolean isAndroid = true;
@BeforeClass(alwaysRun = true)
public void startAppiumServer() {
AppiumServerUtils.getInstance().stopServer();
url = AppiumServerUtils.getInstance().startAppiumServerByDefault();
}
@BeforeMethod
public void setUpDriver() throws MalformedURLException {
DesiredCapabilities capabilities = new DesiredCapabilities();
if (isAndroid) {
capabilities.setCapability("browserName", "chrome");
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.APPIUM);
capabilities.setCapability(CapabilityType.BROWSER_NAME, "chrome");
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "5.1");
capabilities.setCapability("clearSystemFiles", true);
driver = new AndroidDriver<MobileElement>(url, capabilities);
} else {
capabilities.setCapability("browserName", "safari");
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest");
capabilities.setCapability("platformName", "iOS");
capabilities.setCapability("platformVersion", "10.1");
capabilities.setCapability("deviceName", "iPhone SE");
driver = new IOSDriver<MobileElement>(url, capabilities);
}
}
@Test
public void POTest() {
BaiduPageAction.setDriver(driver);
BaiduPageAction.search("Appium");
}
@AfterClass
public void afterTest() {
driver.quit();
}
}
总结
@FindBy 注解和直接使用FindElementBy是一样的,也可以使用FindBys,返回的是一个list和FindElementsBy一样。 需要注意的是,在使用PageFactory初始化Page Element的时候,传入的参数和Selenium的不太一样,第一个参数是AppiumFieldDecorator ,第二个参数是page。
小技巧, 使用driver instanceof AndroidDriver) 判断是什么设备,Android还是iOS,可以把共有部分的代码放倒一起,只需要设置不同的定位方式。
更多关于PO的内容,可以参考官方教程,点击原文
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走
这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助…….
如果你不想再体验一次自学时找不到资料,没人解答问题,坚持几天便放弃的感受的话,可以加入下方我的qq群大家一起讨论交流,里面也有各种软件测试资料和技术交流。