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