轉帖|行業資訊|編輯:鄭恭琳|2020-06-08 14:17:11.677|閱讀 419 次
概述:在使用Selenium一段時間后,如果可以舒適地編寫測試用例,則可以專注于技術和設計原則,以將UI測試自動化提高到一個新的水平。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
在使用Selenium一段時間后,如果可以舒適地編寫測試用例,則可以專注于技術和設計原則,以將UI測試自動化提高到一個新的水平。
本文假定您一直在使用Selenium,并且可以輕松編寫測試用例。您已經經歷了檢查DOM來創建自己的XPath的嘗試。也許您正在使用頁面對象模型。到目前為止,您可能已經非常擅長在Internet上查找解決方案。(如果您需要該部門的幫助,我強烈建議您的同事閱讀這篇文章,并提供一些出色的Selenium技巧。)
接下來的內容分為技術和設計模式。您可能已經對其中一些有所了解。很好,只需跳到您感興趣的部分即可。我將在這里使用Java,但是這些技術和實踐通常應該適用。
更好的定位器
不良的定位器會導致虛假的測試失敗。它們浪費時間并分散測試的預期功能。定位錯誤通常取決于網頁的某些不穩定質量,無論是動態值還是元素在頁面上的位置。當這些質量總是變化時,定位器就會損壞。好的定位器可以工作。他們正確地標識了預期的元素并允許測試完成任務。
此處的關鍵是確定穩定元素的質量,然后選擇這些質量的最小子集以唯一地標識該元素,以減少變化的風險。我們都知道絕對XPath是不好的,但是最好弄清楚為什么。
/html/body/div[25]/div[2]/div/span/span[2]/div/h2/p
對此XPath進行一個小節:div[25]/div[2]。這代表以下品質:
在這一小節中,我們至少使用4種品質來標識元素。如果其中任何一個發生更改,我們的定位器就會損壞。我們沒有理由相信它們不會改變,因為它們都沒有實際描述元素。查看此XPath,我們甚至無法猜測該元素的用途。
反映元素目的的定位符不太可能被破壞。雖然元素的位置或外觀可能會更改,但其功能不應更改。理想情況下,您可以通過查看其定位器來猜測該元素的功能。
//p[contains(@class, “content”)][contains(.,”Guidance for Success”)]
通過上面的操作,很明顯該元素表示描述“成功指南”的內容。請注意,通常用于控制外觀的class屬性也描述了元素的功能。
通常,在元素本身中找不到元素的獨特,穩定的質量。取而代之的是,您需要轉到元素的某些親戚,其功能在DOM中已得到很好的描述。以“成功指導”元素中的上述示例為例。雖然此定位器很好,但是復制文本通常可以更改,如果站點支持多種語言,則復制文本可能不是一個選擇。搜索DOM,我們可能會發現父div具有描述性ID。在這種情況下,我們可能更喜歡以下定位器:
//div[@id=”success_guide”]//p
顯式等待
這里的誘惑是設置一個隱含的等待和希望,以解決大多數問題。問題是這種廣泛的方法將所有情況都相同,甚至沒有解決與等待相關的大多數問題。如果該元素在幾秒鐘后出現但還沒有準備好被單擊該怎么辦?如果該元素存在但被覆蓋層遮蓋了怎么辦?解決方案是使用精心設計的顯式等待。
顯式等待條件的形式為:
WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(/* some condition is true */)
顯式等待功能強大,因為它們具有描述性。它們允許您陳述必要的條件以便繼續進行。一個常見的示例是測試需要單擊某個元素時。僅存在該元素是不夠的;它需要可見并啟用。顯式等待使您可以在測試中直接描述這些要求。我們可以確信在嘗試繼續測試之前已經滿足了條件,從而使您的測試更加穩定:
WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(ExpectedConditions.elementToBeClickable(element))
描述性顯式等待還使您可以編寫針對應用程序行為方面的測試。由于應用程序的行為不會經常更改,因此您可以創建更穩定的測試。以上述示例為例,有必要使一個元素可見并被單擊,但也不必被另一個元素遮擋。如果有一個較大的“加載”疊加層覆蓋您的元素,則單擊將失敗。我們可以創建一個描述此加載行為的顯式等待,并等待滿足必要條件:
WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(ExpectedConditions.invisibilityOf(loadingOverlay))
預期條件
您可能已經注意到上述示例中使用了ExpectedConditions實用程序方法。此類包含編寫Selenium測試時要使用的大量有用條件。如果您還沒有花時間,請花點時間瀏覽完整的API。您通常會使用ExpectedConditions.elementToBeClickable(..)或ExpectedConditions.invisibilityOf(..),但也可能會用到alertIsPresent(),jsReturnsValue(...)或titleContains(..)。您甚至可以使用ExpectedConditions.and(..)或ExpectedConditions.or(..)將條件鏈接在一起。
執行JavaScript
WebDrivers提供了在瀏覽器上下文中執行JavaScript的功能。這是一個簡單的功能,具有令人難以置信的多功能性。可以將其用于常見任務,例如強制頁面滾動到元素,例如:
driver.executeScript("arguments[0].scrollIntoView(false)", element)
它也可以用于利用JQuery或React等應用程序中使用的JavaScript庫。例如,您可以通過調用以下命令來檢查富編輯器中的文本是否已更改:
driver.executeScript(“return EDITOR.instances.editor.checkDirty()”)
executeScript功能為您的測試打開了整個庫API。這些API通常可以提供有用的洞察應用程序狀態的信息,否則將無法使用WebElements進行查詢或不穩定。
使用庫API確實會將您的測試耦合到庫實現。在開發應用程序時,通常可以交換庫,因此以這種方式使用executeScript時需要格外小心。您應該考慮在一個更抽象的界面之后抽象這些特定于庫的調用,以幫助減少測試的不穩定性,例如Bot模式(請參閱下文)。
機器人模式
機器人模式將Selenium API調用抽象為動作。然后可以在整個測試中使用這些操作,以使其更具可讀性和簡潔性。
在本文中,我們已經看到了一些有用的示例。因為在嘗試單擊某個元素之前必須先單擊它,所以我們可能總是希望在每次單擊之前都等待該元素可單擊:
void test() { /* test code */ WebDriverWait wait = new WebDriverWait(driver, 5); wait.until(ExpectedConditions.elementToBeClickable(element)); element.click(); wait.until(ExpectedConditions.elementToBeClickable(element2)); element2.click(); }
可以將代碼抽象為自己的方法,而不是每次測試單擊元素時都寫等待條件:
public class Bot { public void waitAndClick(WebElement element, long timeout) { WebDriverWait wait = new WebDriverWait(driver, timeout); wait.until(ExpectedConditions.elementToBeClickable(element)); element.click(); } }
然后我們的代碼變為:
void test() { /* test code */ bot.waitAndClick(element, 5); bot.waitAndClick(element2, 5); }
該機器人還可以擴展為創建特定于庫的實現。如果應用程序開始使用其他庫,則所有測試代碼可以保持不變,并且僅需要更新Bot:
public class Bot { private WebDriver driver; private RichEditorBot richEditor; public Bot(WebDriver driver, RichEditorBot richEditor) { this.driver = driver; this.richEditor = richEditor; } public boolean isEditorDirty() { richEditor.isEditorDirty(); } } public class RichEditorBot() { public boolean isEditorDirty() { return ((JavascriptExecutor) driver).executeScript(“return EDITOR.instances.editor.checkDirty()”); } } void test() { /* test code */ bot.isEditorDirty(); }
Bot示例可作為WebDriverExtensions庫的一部分以及特定于庫的實現獲得:
Bot模式和Page Object模型可以一起使用。在您的測試中,頂級抽象是表示每個組件功能元素的Page對象。然后,頁面對象包含一個可在頁面對象功能中使用的Bot,從而使它們的實現更簡單易懂:
public class LoginComponent { private Bot bot; @FindBy(id = “login”) private WebElement loginButton; public LoginComponent(Bot bot) { PageFactory.initElements(bot.getDriver(), this); this.bot = bot; } public void clickLogin() { bot.waitAndClick(loginButton, 5); } }
WebDriver Factory
直接在測試代碼中實例化和配置WebDriver實例(例如ChromeDriver或FirefoxDriver)意味著該測試現在有兩個問題:構建特定的WebDriver和測試應用程序。WebDriver Factory通過將所有WebDriver實例化和配置移出測試來分離這些問題。
這可以通過多種方法來實現,但是概念很簡單:創建一個提供完全配置的WebDriver的工廠。在測試代碼中,從工廠獲取WebDriver,而不是直接構造它。現在,有關WebDriver的任何問題都可以在一個地方處理。任何更改都可能在該位置發生,并且每個測試都將獲取更新的WebDriver。
Web Driver Factory項目使用此概念來管理多個測試中WebDrivers的壽命。這個復雜的任務被抽象到工廠,允許測試僅請求具有提供的選項的WebDriver:
//github.com/barancev/webdriver-factory
WebDriver工廠使您可以輕松地在多個瀏覽器中重用單個測試。可以通過外部文件處理所有配置。該測試僅向WebDriver工廠詢問WebDriver的實例,然后工廠將處理詳細信息。一個示例用于在TestNG框架中支持并行網格測試:
//www.swtestacademy.com/selenium-parallel-tests-grid-testng/
擴展頁面對象模型
Page Object模型提供了一個抽象層,該層提供了應用程序組件的功能,同時隱藏了Selenium如何與這些組件交互的詳細信息。這是一種功能強大的設計模式,可以使代碼可重用并且更易于理解。但是,為每個頁面和組件創建一個類可能會有很多開銷。每個類都有一個樣板,然后在類之間共享組件,例如初始化實例以及傳遞WebDriver或Bot對象。可以通過擴展頁面對象模型來減少開銷。
如果您將Bot模式與Page Object模型一起使用,則每個Page Object將需要一個Bot實例。可能看起來像:
public class LoginComponent { private Bot bot; @FindBy(id = “login”) private WebElement loginButton; public LoginComponent(Bot bot) { PageFactory.initElements(bot.getDriver(), this); this.bot = bot; } public void clickLogin() { bot.waitAndClick(loginButton, 5); } }
除了將Bot代碼包含在每個構造函數中之外,還可以將該代碼移到每個組件擴展的另一個類中。這使單個組件代碼可以專注于組件的細節,而不是初始化代碼或傳遞Bot:
public class Component { private Bot bot; public Component(Bot bot) { PageFactory.initElements(bot.getDriver(), this); this.bot = bot; } public Bot getBot() { return bot; } } public class LoginComponent extends Component { @FindBy(id = “login”) private WebElement loginButton; public LoginComponent(Bot bot) { super(bot); } public void clickLogin() { getBot().waitAndClick(loginButton, 5); } }
同樣,對于組件來說,通常需要驗證它是否在正確的時刻被實例化,以便在錯誤使用組件時更容易進行調試。我們可能要檢查標題是否正確:
public class LoginPage extends Component { public LoginPage(Bot bot) { super(bot); bot.waitForTitleContains(“Please login”); } }
我們可以將此檢查移至其他頁面擴展的特定版本的Component中,而不是在每個類中都包括對Bot的調用。這提供了一個小的好處,在創建許多頁面對象時會加起來:
public class TitlePage extends Component { public LoginPage(Bot bot, String title) { super(bot); bot.waitForTitleContains(title); } } public class LoginPage extends TitlePage { public LoginPage(Bot bot) { super(bot, “Please login”); } }
其他庫恰恰為此目的提供了幫助程序類。Selenium Java庫包含LoadableComponent對象,該對象抽象功能并檢查加載頁面的過程:
//github.com/SeleniumHQ/selenium/wiki/LoadableComponent
WebDriverExtensions通過將圍繞頁面對象的許多代碼抽象為注釋,從而創建了更簡單、更易于閱讀的組件,從而進一步發展了:
//github.com/webdriverextensions/webdriverextensions
本文僅涉及一些有用的技術和設計模式。關于這個主題的書已經寫好了。編寫好的測試意味著編寫好的軟件,而編寫好的軟件是一項復雜的任務。
我已經介紹了多年以來我學到的一些信息,以創建更好的Selenium測試。 Parasoft Selenic利用我們的專業知識進一步簡化了任務。有了適當的工具和知識,我們可以改善過程并創建穩定、可讀和可維護的測試。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn