彩票走势图

Selenium专家的重要提示

转帖|行业资讯|编辑:郑恭琳|2020-06-08 14:17:11.677|阅读 342 次

概述:在使用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]。这代表以下品质:

  • 节点在div中的某个位置
  • div是第二个div
  • div直接位于另一个div中
  • div是第25 div

在这一小节中,我们至少使用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库的一部分以及特定于库的实现获得:

  • //github.com/webdriverextensions/webdriverextensions/blob/master/src/main/java/com/github/webdriverextensions/Bot.java
  • //github.com/webdriverextensions/webdriverextensions/blob/master/src/main/java/com/github/webdriverextensions/vaadin/VaadinBot.java

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@pclwef.cn


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP