在selenium中使用AjaxElementLocatorFactory來優化PO模式

語言: CN / TW / HK

之前看到的一篇對於po的改進的文章,非常有啟發,簡單翻譯改寫了一下,希望對大家有幫助。本文基於java,至於其他語言是否有類似的實現,沒有具體研究過。

原文地址: http://www.eliasnogueira.com/better-page-objects-strategy-using-ajaxelementlocatorfactory-with-selenium-and-java

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的話,該項目可以直接做腳手架使用,寫框架的同學可以參考。