POM(Page object Model) & Page Factory: WebDriver
POM是一种设计模式,用于创建web ui元素的对象存储库。在这种模式下,每一个WEB应用下的页面都应该具有一个相关的page的class; 这个class能够寻找到所有页面的webElement, 还包含各种能够实现操作的methods;
这些methods的名字应该作为每一个操作的目标。例如, 一个加载器正在等待要显示的支付网关;那就可以将这个POM中cloass的methods写成 waitForPaymentScreenDisplay()
开始一个UI的自动化测试, 并不是一个很艰难的工作。 我们只需要找到相关的Element, 来完成对应的功能。
考虑下面的一个简单的脚本去登陆一个网站
正如你所见, 无非就是去找到元素, 然后输入对应的值。这是一个简单的脚本,脚本看上去很容易, 但是随着时间的增长,你需要增减越来越多的代码, 事情就变得很复杂。
最主要的问题就是 假如10个不同的而脚本正在使用相同的元素,一旦有任何的改变, 我们就需要改变所有的代码。这即费时还费力。
一个更好的维护脚本的方法就是去创建单独的class , 能够找到对应得元素或者就是去输入值, 或者去验证。这个class能够被重复利用,而且能够容易维护。
这种方法被称为 POM;
优势
- 代码简单而且容易理解
- 重复利用, 还能集成到TestNG和Junit来做单元测试
代码会简洁而且被优化 - methods包含更加容易理解得名称
如何实现POM
简单得POM
POM模式得基本结构就是所有得页面得元素和操作都保存在一个class中;操作都保存在methods中
TestCase:
这个流程涉及到两个界面
- 登陆界面
- homepage界面
分别对应以下两个class
package POMdemo;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage4Baidu {
WebDriver driver;
By user = By.name("uid");
By pwd = By.name("password");
By login = By.name("btnLogin");
By resetBy = By.name("btnReset");
By titletext = By.className("barone"); //如果click正确后,用于验证(Guru99 Bank)是否登陆成功;
//构造函数
public LoginPage4Baidu(WebDriver driver) {
this.driver = driver;
}
public void setUser(String username) {
driver.findElement(user).sendKeys(username);
}
public void setPwd(String pwdString) {
driver.findElement(pwd).sendKeys(pwdString);
}
public void clickLogin() {
driver.findElement(login).click();
}
public void clickResetBy() {
driver.findElement(resetBy).click();
}
public String getLoginTitle() {
String titleString = driver.findElement(titletext).getText();
return titleString;
}
public boolean loginPass() {
return "Guru99 Bank".equals(getLoginTitle());
}
}
package POMdemo;
import java.io.ObjectInputStream.GetField;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class Homepagedemo {
WebDriver driver;
By content = By.xpath("/html/body/table/tbody/tr/td/table/tbody/tr[3]/td");
public Homepagedemo(WebDriver driver) {
this.driver = driver;
}
public String GetTitle() {
return driver.findElement(content).getText();
}
}
调用以上的class,完成测试
package POMdemo;
import java.sql.Driver;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class pomDemo1 {
WebDriver driver;
LoginPage4Baidu loginObj;
Homepagedemo homepageObj;
//setup before test
public void setup() {
String driverLocationString = "D:\\codding\\seleniumTest\\seleniumDemo1\\DriverFolder\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", driverLocationString);
this.driver = new ChromeDriver();
driver.get("http://demo.guru99.com/V4");
}
// test
public void Login2checkName() {
loginObj = new LoginPage4Baidu(this.driver);
loginObj.setUser("mgr123");
loginObj.setPwd("mgr!23");
loginObj.clickLogin();
System.out.println(loginObj.loginPass());
homepageObj = new Homepagedemo(driver);
String title = homepageObj.GetTitle();
System.out.println("login passed and title is"+title);
}
public static void main(String[] args) {
pomDemo1 demo1 = new pomDemo1();
demo1.setup();
demo1.Login2checkName();
demo1.driver.quit();
}
}
page Factory
Page Factory 是一种内建的Page Object 模型概念, 用于Selenuim WebDriver, 但是它是优化的。
在这里,我们遵循页面对象存储和methods的分离概念。另外在PageFatory class的帮助下,我们使用注释 @FindBy 来寻找 WebElement。 我们使用initElements来初始化web元素。
@FindBy 能使用 tagName partialLinkTest,name, linkTest,id,css,className, xpath作为属性。
以下就是一个例子
- login Class
package POMDEMO1;
import java.security.PublicKey;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class loginPage {
WebDriver driver;
@FindBy(name="uid") WebElement username; //类似于driver.findElement(By....)
@FindBy(name="password") WebElement password;
@FindBy(name="btnLogin") WebElement loginIcon;
@FindBy(name="btnReset") WebElement resetIcon;
public loginPage(WebDriver driver) {
this.driver = driver;
//下面的methods会创建所有的webElemets
PageFactory.initElements(driver, this);
}
public void setUsername(String usernamestring) {
username.sendKeys(usernamestring);
}
public void setPWD(String pwdString) {
password.sendKeys(pwdString);
}
public void clickLogin() {
loginIcon.click();
}
public void clickReset() {
resetIcon.click();
}
public void user2login(String user,String pwd) {
this.setUsername(user);
this.setPWD(pwd);
this.clickLogin();
}
}
- homepage class
package POMDEMO1;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class homepage {
WebDriver driver;
@FindBy(xpath="/html/body/table/tbody/tr/td/table/tbody/tr[3]/td") WebElement context;
public homepage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public String getContext() {
return context.getText();
}
}
- TestNG test
package POMDEMO1;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class POMdemo1 {
WebDriver driver;
loginPage loginObj;
homepage homepageObj;
@BeforeTest
public void setup() {
String driverLocationString = "D:\\codding\\seleniumTest\\seleniumDemo1\\DriverFolder\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", driverLocationString);
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://demo.guru99.com/V4");
}
@Test(priority=0)
public void test() {
loginObj = new loginPage(driver);
loginObj.user2login("mgr123", "mgr!23");
homepageObj = new homepage(driver);
Assert.assertTrue(homepageObj.getContext().toLowerCase().contains("manger id : mgr123"));
}
@AfterTest
public void quit() {
driver.quit();
}
}
推荐使用:
AjaxElementLocatorFactory 包含wait时间
waits
在Selenuim中, Waits扮演了一个重要的角色。在这个教程中,你将学习到“implicit”和“Explicit”两种wait
为什么我们需要Waits
其实大部分得页面都是使用Ajax和JavaScript两种。当一个页面在浏览器中被加载得时候,页面中的元素会在不同的时间间隔中被加载。
如果页面没有加载完整, 这不仅会让识别元素变得困难, 而且会报出“ElementNotVisibleException”, 当使用Waits,我们就能有效地避免这个问题。
让我们来模拟一种情景, 我们需要在我们的脚本中使用implicit和explicit两种waits;假定 implicit设置的是20s, 而explicit设置的是10s。
假设我们正在寻找一个元素, 这个元素包含“ExpectedConditions”(外部Wait), 如果这个元素没有在规定的10s内加载好, 那他就会执行implicit Wait(20s); 要是还没有加载好, 就会抛异常。
Implicit Wait
Selenuim WebDriver 参考了Watir的隐式wait的观点。
这种wait会告诉WebDriver去等待确定的时间, 然后再抛出异常:"No Such Element Exception". 默认的时间是0.一旦我们设置了时间, WebDriver会等待。
下面的例子中, 声明了一个隐式的wait, 具体的时间是10s。
driver.manage().timeouts().implicitlyWait(TimeOut, TimeUnit.SECONDS);
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ;
String eTitle = "Demo Guru99 Page";
Explicit Wait
显示的wait就是去告诉WebDriver去等待确定的条件或者是最大的时间, 然后再抛出异常“ElementNotVisibleExpection”
显式的wait是一个很智能的等待, 但是他被应用与特定的element;显式的等待比较好的一个原因是 动态的等待Ajax元素的加载
一旦我们声明一个explicit wait, 我们需要使用“ExpectedConditions”或者我们能配置查看条件(使用 Fluent Wait)满足的频率。
These days while implementing we are using Thread.Sleep() generally it is not recommended to use
WebDriverWait wait = new WebDriverWait(WebDriverRefrence,TimeOut);
driver = new ChromeDriver();
WebDriverWait wait=new WebDriverWait(driver, 20);
String eTitle = "Demo Guru99 Page";
String aTitle = "" ;
// launch Chrome and redirect it to the Base URL
driver.get("http://demo.guru99.com/test/guru99home/" );
//Maximizes the browser window
driver.manage().window().maximize() ;
//get the actual value of the title
aTitle = driver.getTitle();
//compare the actual title with the expected title
if (aTitle.contentEquals(eTitle))
{
System.out.println( "Test Passed") ;
}
else {
System.out.println( "Test Failed" );
}
WebElement guru99seleniumlink;
guru99seleniumlink= wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath( "/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i")));
guru99seleniumlink.click();
}
注意: 以上的例子中我们会等待20秒直到后面的条件满足, 然后我们点击click
下面举例一些条件语句:
1. alertIsPresent()
2. elementSelectionStateToBe()
3. elementToBeClickable()
4. elementToBeSelected()
5. frameToBeAvaliableAndSwitchToIt()
6. invisibilityOfTheElementLocated()
7. invisibilityOfElementWithText()
- presenceOfAllElementsLocatedBy()
- presenceOfElementLocated()
- textToBePresentInElement()
- textToBePresentInElementLocated()
- textToBePresentInElementValue()
- titleIs()
- titleContains()
- visibilityOf()
- visibilityOfAllElements()
- visibilityOfAllElementsLocatedBy()
- visibilityOfElementLocated()
Fluent Wait
流利的等待就是为了告诉webDriver, 抛出异常之前的等待条件, 以及检查条件满足的频率。
频率:以一个固定的交互时间来检测是否条件满足。
让我们考虑一个以不同的时间间隔加载元素的场景。如果我们声明显式等待20秒,该元素可能会在10秒,20秒甚至更长时间内加载。在抛出异常之前它会等到指定的时间。在这种情况下,流畅的等待是理想的等待使用,因为这将尝试以不同的频率找到元素,直到它找到它或最终的计时器用完为止。
Wait wait = new FluentWait(WebDriver reference)
.withTimeout(timeout, SECONDS)
.pollingEvery(timeout, SECONDS)
.ignoring(Exception.class);
package guru.test99;
import org.testng.annotations.Test;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.Test;
public class AppTest3 {
protected WebDriver driver;
@Test
public void guru99tutorials() throws InterruptedException
{
System.setProperty ("webdriver.chrome.driver",".\\chromedriver.exe" );
String eTitle = "Demo Guru99 Page";
String aTitle = "" ;
driver = new ChromeDriver();
// launch Chrome and redirect it to the Base URL
driver.get("http://demo.guru99.com/test/guru99home/" );
//Maximizes the browser window
driver.manage().window().maximize() ;
//get the actual value of the title
aTitle = driver.getTitle();
//compare the actual title with the expected title
if (aTitle.contentEquals(eTitle))
{
System.out.println( "Test Passed") ;
}
else {
System.out.println( "Test Failed" );
}
Wait wait = new FluentWait(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement clickseleniumlink = wait.until(new Function(){
public WebElement apply(WebDriver driver ) {
return driver.findElement(By.xpath("/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i"));
}
});
//click on the selenium link
clickseleniumlink.click();
//close~ browser
driver.close() ;
}
}
频率设置的是5s, 最大的时间是30s,每五秒检查一次元素,不超过30s 要是还是没有就会去报" ElementNotVisibleException"
不推荐使用Thread.Sleep()