本文概述
对于任何敏捷软件开发团队而言, 编写自动化测试不只是一种奢侈。这是一种需要, 并且是在软件开发周期的早期阶段快速发现错误的基本工具。当尚有一个新功能处于开发阶段时, 开发人员可以运行自动化测试, 并查看这些更改如何影响系统的其他部分。本文将解释如何使用Selenium中的Page Object模型来加快自动化测试的速度。
通过测试自动化, 可以降低错误修复的成本, 并全面改善软件质量保证(QA)流程。通过适当的测试, 开发人员甚至有机会在进行质量检查之前找到并解决错误。测试自动化进一步帮助我们自动化不断回归的测试用例和功能。这样, 质量保证有更多时间测试应用程序的其他部分。此外, 这有助于确保生产版本中产品的质量。结果, 我们得到的产品实际上更稳定, 质量检查流程更有效。
Selenium简化了Web应用程序的测试自动化
尽管编写自动化测试对于开发人员和工程师而言似乎是一件容易的事, 但仍然有可能最终导致测试实施不佳以及任何敏捷过程中代码维护的高昂成本。当涉及到测试时, 试图在任何敏捷开发项目中不断交付变更或功能可能会导致成本高昂。更改20个测试所依赖的网页上的一个元素将需要一个经历这20个测试例程, 并更新每个例程以适应这一新引入的更改。这不仅真的很耗时, 而且在尽早实施自动化测试时是一个严重的激励因素。
但是, 如果我们只能在一个地方进行更改, 并且让每个相关的测试例程都使用它, 该怎么办?在本文中, 我们将研究Selenium中的自动化测试, 以及如何使用Page Object模型编写可维护和可重用的测试例程。
Selenium中的页面对象模型
页面对象模型是Selenium中的一种对象设计模式, 其中, 网页表示为类, 页面上的各种元素定义为类中的变量。然后, 所有可能的用户交互都可以实现为类中的方法:
clickLoginButton();
setCredentials(user_name, user_password);
由于类中的命名方法易于阅读, 因此这是实现测试例程的一种优雅方式, 该例程既可读又易于将来维护或更新。例如:
为了支持Page Object模型, 我们使用Page Factory。 Selenium中的Page Factory是Page Object的扩展, 可以以多种方式使用。在这种情况下, 我们将使用Page Factory初始化在网页类或Page Objects中定义的Web元素。
必须先使用Page Factory初始化包含Web元素的Web页面类或Page对象, 然后才能使用Web元素变量。这可以通过使用PageFactory上的initElements函数来简单地完成:
LoginPage page = new LoginPage(driver);
PageFactory.initElements(driver, page);
或者, 甚至更简单:
LoginPage page = PageFactory.intElements(driver, LoginPage.class)
或者, 在网页类构造函数中:
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
Page Factory将基于配置的”定位符”, 使用对实际网页上相应元素的引用来初始化每个WebElement变量。这是通过使用@FindBy批注完成的。使用此批注, 我们可以定义用于查找元素的策略, 以及用于标识元素的必要信息:
@FindBy(how=How.NAME, using="username")
private WebElement user_name;
每次在此WebElement变量上调用方法时, 驱动程序将首先在当前页面上找到它, 然后模拟交互。如果我们使用的是简单页面, 我们知道每次寻找时都会在页面上找到该元素, 并且我们也知道我们最终将离开该页面而不返回该页面, 因此可以进行缓存通过使用另一个简单的注释来查找字段:
@FindBy(how=How.NAME, using="username")
@CacheLookup
private WebElement user_name;
WebElement变量的整个定义可以用更简洁的形式代替:
@FindBy(name="username")
private WebElement user_name;
@FindBy批注支持其他一些使事情变得容易一些的其他策略:
id, name, className, css, tagName, linkText, partialLinkText, xpath
@FindBy(id="username")
private WebElement user_name;
@FindBy(name="passsword")
private WebElement user_password;
@FindBy(className="h3")
private WebElement label;
@FindBy(css="#content")
private WebElement text;
初始化后, 这些WebElement变量即可用于与页面上的相应元素进行交互。例如, 以下代码:
user_password.sendKeys(password);
…将给定的击键序列发送到页面上的password字段, 它等效于:
driver.findElement(By.name("user_password")).sendKeys(password);
继续前进, 你会经常遇到需要在页面上查找元素列表的情况, 那就是@FindBys派上用场的情况:
@FindBys(@FindBy(css="div[class=’yt-lockup-tile yt-lockup-video’]")))
private List<WebElement> videoElements;
上面的代码将找到所有具有两个类名称” yt-lockup-tile”和” yt-lockup-video”的div元素。通过将其替换为以下内容, 我们可以进一步简化此操作:
@FindBy(how=How.CSS, using="div[class=’yt-lockup-tile yt-lockup-video’]")
private List<WebElement> videoElements;
此外, 你可以将@FindAll与多个@FindBy批注一起使用, 以查找与任何给定定位符匹配的元素:
@FindAll({@FindBy(how=How.ID, using="username"), @FindBy(className="username-field")})
private WebElement user_name;
现在, 我们可以将网页表示为Java类, 并使用Page Factory轻松地初始化WebElement变量, 现在是时候该看看如何使用Page Object模式和Page Factory编写简单的Selenium测试了。
Java中的简单Selenium测试自动化项目
对于我们的Page Object模型教程, 让我们自动化开发人员注册srcmini。为此, 我们需要自动执行以下步骤:
-
请访问www.srcmini02.com
-
点击”作为开发者申请”按钮
-
首先在门户页面上检查它是否已打开
-
点击”加入srcmini”按钮
-
填写表格
-
点击”加入srcmini”按钮提交表格
设置项目
-
下载并安装Java JDK
-
下载并安装InteliJ Idea
-
创建一个新的Maven项目
-
将” Project SDK”链接到你的JDK, 例如:Windows上的” C:\ Program Files \ Java \ jdkxxx”
-
设置groupId和artifactId:
<groupId>SeleniumTEST</groupId>
<artifactId>Test</artifactId>
- 在项目POM文件中添加依赖项Selenium和JUnit Maven
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-firefox-driver</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-support</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
</dependencies>
通过在Google和Selenium网站上搜索JUnit Maven可以找到的最新版本号替换Selenium版本和JUnit版本。
此时, 如果启用了自动构建, 则依赖项应开始自动下载。如果没有, 只需激活IntelliJ Idea IDE右侧的Maven Projects面板下的Plugins> install> install:install。
一旦启动了项目, 我们就可以在” src / test / java”下开始创建测试包。将包命名为” com.srcmini”, 并在其下创建另外两个包:” com.srcmini.webpages”和” com.srcmini.tests”。
我们将页面对象/页面工厂类保留在” com.srcmini.webpages”下, 并将测试例程保留在” com.srcmini.tests”下。
现在, 我们可以开始创建Page Object类。
主页页面对象
我们需要实施的第一个对象是srcmini的主页(www.srcmini02.com)。在” com.srcmini.webpages”下创建一个类, 并将其命名为” HomePage”。
package com.srcmini.webpages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;
public class HomePage {
private WebDriver driver;
//Page URL
private static String PAGE_URL="https://www.srcmini02.com";
//Locators
//Apply as Developer Button
@FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER")
private WebElement developerApplyButton;
//Constructor
public HomePage(WebDriver driver){
this.driver=driver;
driver.get(PAGE_URL);
//Initialise Elements
PageFactory.initElements(driver, this);
}
public void clickOnDeveloperApplyButton(){
developerApplyButton.click();
}
}
确定元素定位器
在srcmini主页上, 我们特别关注一个元素, 那就是”应用为开发者”按钮。我们可以通过匹配文本来找到该元素, 这就是我们在上面所做的。在将网页建模为”页面对象”类时, 查找和标识元素通常会变得很繁琐。使用Google Chrome或Firefox的调试工具, 可以使此过程变得更加容易。通过右键单击页面上的任何元素, 可以激活上下文菜单中的”检查元素”选项, 以查找有关该元素的详细信息。
一种常见的方法(也是我的首选方法)是使用Firefox的FireBug扩展程序以及Selenium中的Firefox Web驱动程序来查找元素。安装并启用FireBug扩展程序后, 可以右键单击页面并选择”使用FireBug检查元素”以打开FireBug。在FireBug的HTML选项卡中, 你可以复制页面上任何元素的XPath, CSS路径, 标记名称或” Id”(如果可用)。
通过复制以上屏幕快照中元素的XPath, 我们可以在Page Object中为其创建WebElement字段, 如下所示:
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1")
WebElement heading;
为了简单起见, 我们可以在此处使用标签名称” h1″, 只要它唯一地标识我们感兴趣的元素即可:
@FindBy(tagName = "h1")
WebElement heading;
DeveloperPortalPage页面对象
接下来, 我们需要一个页面对象, 该对象代表开发者门户页面, 单击”作为开发者申请”按钮可以访问该页面。
在此页面上, 我们有两个有趣的元素。为了确定页面是否已加载, 我们要验证标题的存在。而且, 我们还需要一个用于” Join srcmini”按钮的WebElement字段。
package com.srcmini.webpages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class DeveloperPortalPage {
private WebDriver driver;
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1")
private WebElement heading;
@FindBy(linkText = "JOIN srcmini")
private WebElement joinsrcminiButton;
//Constructor
public DeveloperPortalPage (WebDriver driver){
this.driver=driver;
//Initialise Elements
PageFactory.initElements(driver, this);
}
//We will use this boolean for assertion. To check if page is opened
public boolean isPageOpened(){
return heading.getText().toString().contains("Developer portal");
}
public void clikOnJoin(){
joinsrcminiButton.click();
}
}
DeveloperApplyPage页面对象
最后, 对于该项目的第三个也是最后一个页面对象, 我们定义一个代表包含开发人员应用程序表单的页面的对象。由于这里我们必须处理许多表单字段, 因此我们为每个表单字段定义一个WebElement变量。我们通过每个字段的” id”找到它们, 并为每个字段定义特殊的设置方法, 以模拟相应字段的击键。
package com.srcmini.webpages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class DeveloperApplyPage {
private WebDriver driver;
@FindBy(tagName = "h1")
WebElement heading;
@FindBy(id="developer_email")
WebElement developer_email;
@FindBy(id = "developer_password")
WebElement developer_password;
@FindBy(id = "developer_password_confirmation")
WebElement developer_password_confirmation;
@FindBy(id = "developer_full_name")
WebElement developer_full_name;
@FindBy(id = "developer_skype")
WebElement developer_skype;
@FindBy(id ="save_new_developer")
WebElement join_srcmini_button;
//Constructor
public DeveloperApplyPage(WebDriver driver){
this.driver=driver;
//Initialise Elements
PageFactory.initElements(driver, this);
}
public void setDeveloper_email(String email){
developer_email.clear();
developer_email.sendKeys(email);
}
public void setDeveloper_password(String password){
developer_password.clear();
developer_password.sendKeys(password);
}
public void setDeveloper_password_confirmation(String password_confirmation){
developer_password_confirmation.clear();
developer_password_confirmation.sendKeys(password_confirmation);
}
public void setDeveloper_full_name (String fullname){
developer_full_name.clear();
developer_full_name.sendKeys(fullname);
}
public void setDeveloper_skype (String skype){
developer_skype.clear();
developer_skype.sendKeys(skype);
}
public void clickOnJoin(){
join_srcmini_button.click();
}
public boolean isPageOpened(){
//Assertion
return heading.getText().toString().contains("Apply to join our network as a developer");
}
}
编写简单的Selenium测试
通过将代表页面的Page Object类和用户交互作为其方法, 我们现在可以将我们的简单测试例程编写为一系列简单的方法调用和断言。
package com.srcmini.tests;
import com.srcmini.webpages.DeveloperApplyPage;
import com.srcmini.webpages.DeveloperPortalPage;
import com.srcmini.webpages.HomePage;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import java.net.URL;
import java.util.concurrent.TimeUnit;
public class ApplyAsDeveloperTest {
WebDriver driver;
@Before
public void setup(){
//use FF Driver
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
@Test
public void applyAsDeveloper() {
//Create object of HomePage Class
HomePage home = new HomePage(driver);
home.clickOnDeveloperApplyButton();
//Create object of DeveloperPortalPage
DeveloperPortalPage devportal= new DeveloperPortalPage(driver);
//Check if page is opened
Assert.assertTrue(devportal.isPageOpened());
//Click on Join srcmini
devportal.clikOnJoin();
//Create object of DeveloperApplyPage
DeveloperApplyPage applyPage =new DeveloperApplyPage(driver);
//Check if page is opened
Assert.assertTrue(applyPage.isPageOpened());
//Fill up data
applyPage.setDeveloper_email("[email protected]");
applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test");
applyPage.setDeveloper_password("password123");
applyPage.setDeveloper_password_confirmation("password123");
applyPage.setDeveloper_skype("automated_test_skype");
//Click on join
//applyPage.clickOnJoin();
}
@After
public void close(){
driver.close();
}
}
运行测试
此时, 你的项目结构应如下所示:
如果要运行测试, 请从树中选择” ApplyAsDeveloperTest”, 右键单击它, 然后选择”运行’ApplyAsDeveloperTest”。
运行测试后, 你可以在IDE的左下角看到结果:
总结
通过Page Object和Page Factory, 可以轻松地在Selenium中对网页进行建模和自动测试, 并使开发人员和QA的工作变得更加简单。如果操作正确, 则可以在整个测试套件中重用这些Page Object类, 使你有机会尽早为你的项目实施自动化Selenium测试, 而不会影响敏捷开发。通过在页面对象模型中抽象出用户交互, 并使测试例程保持简洁明了, 你可以轻松地使测试套件适应不断变化的需求。
希望我能向你展示如何编写易于维护的美观且干净的测试代码。我将以我最喜欢的质量检查报价结束本文:
三思而后行, 编码一次!
相关:使用无头浏览器进行网页爬取:木偶教程