PO模式是page object factory設計模式的簡稱,主要是以頁面為次元來聚合一些元素的定位,讓代碼有更好的維護性和重用性,具體細節可以看這裡:https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models。這裡是官方文檔,非常值得精讀。
Page Factory
如果你已經對po很熟悉了,下面的内容可以放心跳過。
下面是最基本的Page Factory套路,本質上是按頁面去封裝元素定位和操作。
public class PageObjectExample {
private final WebDriver driver;
public PageObjectExample(WebDriver driver) {
this.driver = driver;
}
public void login(String email, String password) {
driver.findElement(By.id("email")).sendKeys(email);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.name("next")).click();
}
}
上面的代碼隻能說懂得都懂,不過這裡有個問題,在login方法裡,我們頻繁使用 driver.findElement 方法,這會顯得有一些的啰嗦,下面是改進版本,優雅了很多。
public class PageObjectExample {
@FindBy(id = "email")
private WebElement email;
@FindBy(id = "password")
private WebElement password;
@FindBy(name = "next")
private WebElement next;
public PageObjectExample(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void login(String email, String password) {
this.email.sendKeys(email);
this.password.sendKeys(password);
next.click();
}
}
這裡要注意的是在進行初始化的時候,需要調用 PageFactory.initElements(driver, this);
本質上這行代碼的作用是将上面的注解@FindBy轉換成基本的 findElement 形式。
參考資料:https://github.com/SeleniumHQ/selenium/wiki/PageFactory
PO模式的常見問題
等待政策。selenium提供了3種測試,分别是顯示,隐式,以及流利等待(fluent wait),一般情況下隐式等待是不推薦的。
在po中,我們會在2種情況下用到等待,分别是初始化po對象時以及在action方法時,action方式其實就是指的包含有元素操作的方法。
初始化時等待
如果操作的對象在頁面加載的時候就會渲染完畢的話,那麼在初始化時等待将會是一個非常好的實踐。總體的實作思路是在初始化時告訴po,我們明确希望等待哪個元素出現,并且最多等待多久。
public class PageObjectExample {
private WebDriver driver;
@FindBy(id = "email")
private WebElement email;
@FindBy(id = "password")
private WebElement password;
@FindBy(name = "next")
private WebElement next;
public PageObjectExample(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
new WebDriverWait(driver, Duration.of(5, ChronoUnit.SECONDS))
.until(ExpectedConditions.visibilityOf(email));
}
public void login(String email, String password) {
this.email.sendKeys(email);
this.password.sendKeys(password);
next.click();
}
}
上面的代碼中我們希望在初始化頁面對象時,其實也就是在頁面加載的時候明确等待email這個元素出現,逾時時間為5s。
操作時等待
這裡的做法是先等待再操作,比如
public class PageObjectExample {
private WebDriver driver;
@FindBy(id = "email")
private WebElement email;
@FindBy(id = "password")
private WebElement password;
@FindBy(name = "next")
private WebElement next;
public PageObjectExample(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void fillEmail(String email) {
new WebDriverWait(driver, Duration.of(5, ChronoUnit.SECONDS))
.until(ExpectedConditions.visibilityOf(this.email));
this.email.sendKeys(email);
}
}
其實就是把等待的代碼換了個位置而已。
簡化等待操作
上面的方式可以運作得很好,不過還是有點太啰嗦了,下面的做法可以緩解一下
• 在 PageFactory.initiElements 中調用 AjaxElementLocatorFactory 方法,這樣可以無腦等待; • 繼承PageFactory.initiElements方法,這樣子類裡就不用反複寫了
下面是基本的代碼
public class PageObjectExample {
private WebDriver driver;
// WebElements ignored
public PageObjectExample(WebDriver driver) {
PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
}
}
使用繼承來簡化代碼
public abstract class AbstractPageObject {
private WebDriver driver;
protected AbstractPageObject(WebDriver driver) {
PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
}
}
public class PageObject extends AbstractPageObject {
// WebElements ignored
protected PageObject(WebDriver driver) {
super(driver);
}
// action steps ignored
}
總結
大家可以通過https://github.com/eliasnogueira/selenium-java-lean-test-architecture這個項目來熟悉上面的概念,如果你使用java的話,該項目可以直接做腳手架使用,寫架構的同學可以參考。