越写悦快乐之如何利用POM抽象基于Selenium构建的自动化页

我们上一篇文章使用Selenium的参数化注解构建多个测试套件,配合TestNG可以在命令行或者终端运行测试命令,今天的文章我们利用PageObjects来抽象你的页面对象,通过Page Objects、Page Sections、Page Element、PageFactory、PageFilters和Dependency Injection来抽象页面元素并结合Selenium自动化你的项目,接下来我为大家分享一下我的学习经验,希望对大家有所帮助。
开发环境
- Window 10.0.17763
- Java 8.0.191
- Chrome 73.0.3683
- IDEA 2018.3
Maven Version

Build Version
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>me.weitao.app</groupId>
<artifactId>selenium-pom-tour</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>selenium-pom-tour</name>
<description>A Selenium POM Tour for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<repositories>
<repository>
<id>AliYun Repository</id>
<name>AliYun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>4.0.9</version>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports-testng-adapter</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>uk.sponte.automation</groupId>
<artifactId>selenium-pom</artifactId>
<version>3.0.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>${basedir}/src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
什么是POM
POM是页面对象模型(Page Object Model)的简称,也是Selenium框架内置实现页面的抽象,我们可以利用它枚举、操作、查找页面的元素,通过驱动的方式来执行页面的行为。
原理解读
构建步骤
我们的示例项目以Spring Boot为基准项目来演示POM的使用
添加依赖
构建基础Page对象
为了统一处理页面驱动和驱动等待等属性,我们声明了driver
和wait
两个属性来存储,并声明构造方法和一些元素操作的方法,下面给出该文件的示例代码,仅供大家参考。
package me.weitao.app.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
/**
* @author Watony Weng
*/
public class BasePage {
public WebDriver driver;
public WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
wait = new WebDriverWait(driver, 15);
}
public void waitVisibility(By elementBy) {
wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(elementBy));
}
public void click(By elementBy) {
waitVisibility(elementBy);
driver.findElement(elementBy).click();
}
public void writeText(By elementBy, String text) {
waitVisibility(elementBy);
driver.findElement(elementBy).sendKeys(text);
}
public String readText(By elementBy) {
waitVisibility(elementBy);
return driver.findElement(elementBy).getText();
}
public void assertEquals(By elementBy, String expectedText) {
waitVisibility(elementBy);
Assert.assertEquals(readText(elementBy), expectedText);
}
}
创建百度搜索实现类
假设我们想通过搜索关键字的方式打开百度首页,并输入搜索关键字Selenium PageObjects with Java
,然后点击搜索结果页的链接并跳转到我们的博客园参考页面,最后通过Assert.assertEquals
验证页面的标题,下面给出该文件的示例代码,仅供大家参考。
package me.weitao.app.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
/**
* @author Watony Weng
*/
public class HomePage extends BasePage {
private static final Logger LOG = LoggerFactory
.getLogger(HomePage.class);
private static final String BASE_URL = "http://www.baidu.com/";
public HomePage(WebDriver driver) {
super(driver);
}
/**
* 页面搜索
*
* @param keyword 搜索关键字
*/
public void testSearch(String keyword) {
driver.get(BASE_URL);
writeText(By.id("kw"), keyword);
click(By.id("su"));
LOG.info("after search url is :{}", driver.getCurrentUrl());
}
/**
* 链接跳转
*/
public void testClick() {
driver.get(BASE_URL + "s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=Selenium%20PageObjects%20with%20Java&rsv_pq=90d822e0000248fe&rsv_t=14510I%2FJ6J5FZIjaYSQoQZ0hlAO4TG2Oa420IuApxqU%2FaK4dCRj4WZPCWUI&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_sug3=30&inputT=347&rsv_sug4=347");
click(By.xpath("//*[@id=\"4\"]/h3/a"));
LOG.info("after click url is :{}", driver.getCurrentUrl());
}
/**
* 标题验证
*/
public void testTitle() {
driver.get("https://www.cnblogs.com/NaCl/p/Selenium.html");
Assert.assertEquals(readText(By.xpath("//*[@id=\"cb_post_title_url\"]")), "Selenium+PageObject+Java实现测试用例");
LOG.info("after title url is :{}", driver.getCurrentUrl());
}
}
创建测试基类
我们编写完页面对象后,接下来需要通过TestNG
的注解来调用我们的Page
对象的方法,比如搜索、点击、跳转等方法,下面给出该文件的示例代码,仅供大家参考。
package me.weitao.app.tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import java.util.concurrent.TimeUnit;
/**
* @author Watony Weng
*/
public class BaseTest {
public WebDriver driver;
@BeforeClass
public void setUp() {
System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--start-maximized");
driver = new ChromeDriver(chromeOptions);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@AfterClass
public void tearDown() {
driver.quit();
}
}
创建测试类
我们实现基类并获取driver
属性,然后定义测试方法来测试Page对象,下面给出该文件的示例代码,仅供大家参考。
package me.weitao.app.tests;
import com.aventstack.extentreports.testng.listener.ExtentITestListenerClassAdapter;
import me.weitao.app.pages.HomePage;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/**
* @author Watony Weng
*/
@Listeners({ExtentITestListenerClassAdapter.class})
public class HomeTest extends BaseTest {
private HomePage homePage;
@BeforeMethod
public void before() {
homePage = new HomePage(driver);
}
@Test(priority = 1)
public void testHomeSearch() {
homePage.testSearch("Selenium PageObjects with Java");
}
@Test(priority = 2)
public void testHomeClick() {
homePage.testClick();
}
@Test(priority = 3)
public void testHomeTitle() {
homePage.testTitle();
}
@AfterMethod
public void after() {
homePage = null;
}
}
配置Test文件
完成以上的步骤后,我们可以在testng.xml
文件中配置测试套件并配置可以启动的测试类,下面给出该文件的示例代码,仅供大家参考。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Selenium Automation Test Suite">
<test name="PageObjects Test Case">
<packages>
<package name="me.weitao.app.tests">
<exclude name="me.weitao.app.tests.BaseTest"/>
</package>
</packages>
</test>
</suite>
配置测试报告选项
我们在pom文件中配置了extentreports
,可以生成测试报告,配合maven-surefire-plugin
查看可以生成不同的测试报告,这里给出extent.properties
和html-config.xml
配置文件的内容如下:
extent.reporter.avent.start=false
extent.reporter.bdd.start=false
extent.reporter.cards.start=false
extent.reporter.email.start=false
extent.reporter.html.start=true
extent.reporter.klov.start=false
extent.reporter.logger.start=false
extent.reporter.tabular.start=false
extent.reporter.avent.config=
extent.reporter.bdd.config=
extent.reporter.cards.config=
extent.reporter.email.config=
extent.reporter.html.config=src/test/resources/html-config.xml
extent.reporter.klov.config=
extent.reporter.logger.config=
extent.reporter.tabular.config=
extent.reporter.avent.out=target/extent-reports/AventReport/
extent.reporter.bdd.out=target/extent-reports/BddReport/
extent.reporter.cards.out=target/extent-reports/CardsReport/
extent.reporter.email.out=target/extent-reports/EmailReport/ExtentEmail.html
extent.reporter.html.out=target/extent-reports/HtmlReport/ExtentHtml.html
extent.reporter.logger.out=target/extent-reports/LoggerReport/
extent.reporter.tabular.out=target/extent-reports/TabularReport/
<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
<configuration>
<!-- report theme -->
<theme>standard</theme>
<!-- document encoding -->
<encoding>UTF-8</encoding>
<!-- enable or disable timeline on dashboard -->
<enableTimeline>true</enableTimeline>
<!-- protocol for script and stylesheets -->
<protocol>https</protocol>
<!-- title of the document -->
<documentTitle>Extent and TestNG Integration</documentTitle>
<!-- report name - displayed at top-nav -->
<reportName>Extent Report</reportName>
<!-- create a report with all artifacts stored locally -->
<enableOfflineMode>true</enableOfflineMode>
</configuration>
</extentreports>
运行测试命令
我们最后在Idea
的Terminal窗口执行以下命令,即可看到测试用例的运行结果。
npm clean test
查看测试报告
我们在
target/extent-reports/HtmlReport
目录下即可看到生成的测试报告。
参考
个人感想与收获
通过以上知识点的学习,我们知道了如何通过Page对象抽象我们的业务页面,然后借助Driver来控制浏览器,最后完成页面元素的一系列操作,我们自此完成了基本的自动化操作,再配合DataProvider更能把页面元素抽象化和实例化,让我们能更加从容地使用Selenium,借助这个工具完成能更方便地服务于我们的业务逻辑,接下来我们会继续探索Selenium这个框架,从其他方面优化、抽象、提升我们的测试水平,提升软件质量,服务更多的用户。希望大家保持学习的热情,继续充实自己的技能包,打造更高要求的研发团队,向更高更快的方向进发。