【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

你并不总希望在真正浏览器中进行测试:Selenium 来营救

  • 2019-01-15
  • 本文字数:4893 字

    阅读完需:约 16 分钟

你并不总希望在真正浏览器中进行测试:Selenium来营救

Selenium 是一个在 Web 浏览器中进行自动化测试的强大工具。虽然 Selenium Web 驱动程序支持所有主流的浏览器,但你并不总是希望在真正的浏览器中进行测试。

本文要点

  • Selenium 运行时没有 UI 界面;

  • 不再支持 PhantomJS;

  • JBrowser 驱动程序是一个支持 Java 8 的低开销选项;

  • 如果你需要 Java 11 支持,那么当前所有的 Java Selenium 驱动程序都需要安装一个真正的浏览器。


Selenium是一个在 Web 浏览器中进行自动化测试的强大工具。虽然 Selenium Web 驱动程序支持所有的主流浏览器,但你并不总是希望在真正的浏览器中进行测试。Selenium 来营救!本文中的示例来自GitHub库。所有示例都使用 JUnit 5 和 Maven 运行。每个示例都有 Java 11 和 Java 8 支持说明。

使用 Selenium 有什么好处?

Selenium 在运行时没有用户界面(UI)。使用 Selenium 进行测试的一大好处是性能——因为 Selenium 没有 UI,所以它们比真正的浏览器快。


一些 Selenium 还有另外一个优势——依赖。当在像 Jenkins 这样的持续集成服务器上进行测试时,机器可能没有安装真正的浏览器。根据你的环境,你可能没有权限安装一个。


另一方面,需要安装“真正的”浏览器的 Selenium 浏览器非常适合开发。例如,Chrome 和 Firefox 都可以在无头模式下运行。在调试 Selenium 脚本时,临时关闭无头模式并观看程序运行非常有用。这样你就可以直观地看到哪里出了问题。

HtmlUnitDriver——最初的 Selenium 驱动程序

过去,Selenium 带有一个内置的 Selenium 驱动程序 HtmlUnitDriver。虽然这个驱动程序仍然受支持,但它现在是一个单独的依赖项,并且不出所料地使用了 Html Unit 框架。在单页应用程序和主要基于 AJAX 的页面出现之前,这个驱动程序是一个非常好的选择。你可以选择是否运行页面 JavaScript,而且它在内存中运行并且非常快。对于包含大量 HTML 数据的 Web 页面来说,这仍然是一个不错的选择。


下面的代码展示了如何在 HtmlUnitDriver 中使用 Selenium 运行基本的测试。它之所以有效,是因为 InfoQ 的主页设计成了没有 JavaScript 也能正常工作。这个例子在 GitHub 库里,有Java 8Java 11版本。


package com.infoq.selenium;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import java.util.Set;import java.util.stream.Collectors;
public class HtmlUnitSeleniumIT {
protected WebDriver driver;
// ----------------------------------------------------
@BeforeEach public final void connect() { driver = new HtmlUnitDriver(); //driver.setJavascriptEnabled(true); }
@AfterEach public final void closeDriver() { if (driver != null) { driver.quit(); } }
@Test void qconDates() { driver.get(“https://www.infoq.com“);
Set<String> newYorkCity = driver.findElements(By.className(“qcon”)) .stream() .map(element -> element.getAttribute(“innerText”)) .filter(city -> city.trim().startsWith(“New York”)) .collect(Collectors.toSet()); assertEquals(1, newYorkCity.size(), “New York is an upcoming city”);
}}

复制代码


然而,取消启用 JavaScript 这行代码的注释就是另外一回事了。它会在抛出一堆 JavaScript 警告之后失败并报错:EcmaError: TypeError:无法调用未定义方法“then”。


由于许多页面在没有 JavaScript 的情况下根本无法加载,因此需要一个能更好地支持 JavaScript 的 Selenium 驱动程序。

PhantomJS

多年来,PhantomJS是一个很好的选择。它轻量级、无头,并且有很好的 JavaScript 支持。然而,2017 年 4 月,维护者退出,2018 年 3 月,该项目被正式放弃。我想念它。


通过阅读声明和评论可以发现,其意图显然是转移到 Chrome 驱动程序。对于任何新东西,我都不建议使用 PhantomJS。除了不受支持之外,我还将两个项目切换到 Chrome 驱动程序,因为对于当前大部分 JavaScript 库,PhantomJS 都不能很好地处理其中的 JavaScript。因为 Chrome 驱动程序使用的是真正的浏览器,所以这不是问题。

Chrome 驱动程序

Chrome 提供了一种无头模式,总体效果很好。最大的缺点是你需要能够安装 Chrome。你不需要 UI,但是并不一定能够安装软件。


Chrome 驱动程序也需要下载一个可执行文件。我在这里用了一点小技巧。我将可执行文件保存在与项目相同的目录中(或者保存在二进制存储库里,并将其复制到工作区中)。然后,我让 Java 测试本身设置权限。类似地,我让 Java 测试将 Java 流程中的系统属性设置为该位置。我知道这有点像作弊,但它确实让我几乎可以控制所有事情。“几乎”是因为它仍然需要安装 Chrome 本身。对我的个人项目,这非常有效。不过,当与他人共享代码时,它会崩溃,因为他们也需要下载可执行文件。


GitHub 库里有Java 8Java 11两个版本。两者都要求你下载可执行文件并将其替换到 chrome-driver 目录中。


package com.infoq.selenium;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import static org.junit.jupiter.api.Assertions.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.stream.Collectors;
public class ChromeSeleniumIT {
private static final boolean HEADLESS = true;
private static final String CHROME_DRIVER_DIRECTORY = “chrome-driver”;
protected WebDriver driver;
// ----------------------------------------------------
@BeforeEach
public final void connect() {
Path chrome = Paths.get(CHROME_DRIVER_DIRECTORY + “/chromedriver”);
chrome.toFile().setExecutable(true);
System.setProperty(“webdriver.chrome.driver”, chrome.toAbsolutePath().toString());
ChromeOptions chromeOptions = new ChromeOptions();
if (HEADLESS) {
chromeOptions.addArguments(“--headless”);
}
driver = new ChromeDriver(chromeOptions);
// https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/27
((JavascriptExecutor) driver).executeScript(“window.alert = function(msg) { }“);
((JavascriptExecutor) driver).executeScript(“window.confirm = function(msg) { }“);
}
@AfterEach
public final void closeDriver() {
if (driver != null) {
driver.quit();
}
}
@Test
void qconDates() {
driver.get(“https://www.infoq.com”);
Set<String> newYorkCity = driver.findElements(By.className(“qcon”))
.stream()
.map(element -> element.getAttribute(“innerText”))
.filter(city -> city.trim().startsWith(“New York”))
.collect(Collectors.toSet());
assertEquals(1, newYorkCity.size(), “New York is an upcoming city”);
}

复制代码


这两行执行脚本用于解决我正在测试的另一个应用程序中的提示问题。我已经告诉驱动程序忽略它们。(虽然这里不需要它,但我发现我在任何使用 Chrome 驱动程序的地方都使用了它,所以我永远都不需要解决这个问题,这已经够痛苦的了!)


使用 Chrome 驱动程序的另一个缺点是它需要定期更新以支持 Chrome 的后续版本。

Gecko 驱动程序

Chrome 是第一个进行无头浏览器测试的,所以这是我最熟悉的一个。然而,Firefox 也有无头模式。就像 Chrome 一样。你可以下载Gecko驱动程序,并在你的 pom.xml 中使用 selenium-firefox-driver。

JBrowser 驱动程序

虽然我喜欢 Chrome 驱动程序,但它确实需要安装 Chrome。我有一个项目,我每天运行一个检查,看看 Oracle 认证目标是否有变化。在我运行这项检查的服务器上没有安装 Chrome。最近,Oracle 更新了他们的网站,更多地使用了 AJAX。PhantomJS 不再满足我的需求,所以我开始寻找一个更现代的驱动程序。


我找到了JBrowser驱动程序。该项目去年一直有定期的提交,包括针对 Selenium 版本的更新。它有良好的许可协议(Apache 2)。1.0.0 版本刚刚在 2018 年夏天发布。不过,1.0 之前的版本已经发布近 3 年了。


JBrowser 驱动程序最大的缺点是目前只支持 Oracle JDK Java 8。这个版本的 Java 将分别在 2019 年 1 月和 2020 年 12 月停止为企业用户和个人用户提供补丁。


关于 Java FX,请注意:


  • 在 Java 7 中,Java FX 是单独下载的;

  • 在 Java 8 中,Java FX 是 Oracle JDK 的组成部分,但不是 Open JDK 的组成部分;

  • 在 Java 11 中,Java FX 可以通过 Maven 依赖使用OpenJFX免费获得;

  • 同样,在 Java 11 中,Robot.java 类所在的包从com.sun.glass.ui变成了javafx.scene.robot。这意味着你不能只在 Open JDK 11 中使用 OpenJFX 的 JavaFX 版本,就期望 JBrowser 驱动程序能够正常工作。


代码很简单,Java 8版本在 GitHub 库中。



import com.machinepublishers.jbrowserdriver.JBrowserDriver;
import com.machinepublishers.jbrowserdriver.Settings;
import com.machinepublishers.jbrowserdriver.Timezone;
import com.machinepublishers.jbrowserdriver.UserAgent;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class JBrowserSeleniumIT {
protected WebDriver driver;
// ----------------------------------------------------
@BeforeEach
public final void connect() {
driver = new JBrowserDriver(Settings.builder()
.timezone(Timezone.AMERICA_NEWYORK)
.userAgent(UserAgent.CHROME).build());
// says 120 but is really 0
driver.manage().timeouts().pageLoadTimeout(120, TimeUnit.SECONDS);
}
@AfterEach
public final void closeDriver() {
if (driver != null) {
driver.quit();
}
}
@Test
void qconDates() {
driver.get(“https://www.infoq.com“);
Set<String> newYorkCity = driver.findElements(By.className(“qcon”))
.stream()
.map(element -> element.getAttribute(“innerText”))
.filter(city -> city.trim().startsWith(“New York”))
.collect(Collectors.toSet());
assertEquals(1, newYorkCity.size(), “New York is an upcoming city”);
}}
复制代码

小结——可选驱动对比

使用 Java 和 Selenium 进行无头浏览器测试有很多选择。就像任何良好的工程问题一样,需要在它们之间进行权衡。这张表列出了你的主要选项。我的经验是,如果你需要在没有安装真正浏览器的情况下运行无头 Selenium 驱动程序,那么你需要暂时使用 Java 8。


关于作者


Jeanne Boyarsky 是一名 Java 开发人员和兼职 ScrumMaster。她与人合著了 Wiley 出版的 OCA/OCP 8 认证书籍,并将针对认证的下一个版本进行更新。除了在 CodeRanch 做志愿者外,她还在一个高中机器人团队中指导程序员,并获得了导师奖。Jeanne 曾在 JavaOne、QCon、DevNexus 和 SpringOne 等会议上演讲。


查看英文原文:https://www.infoq.com/articles/headless-selenium-browsers


2019-01-15 18:118272
用户头像

发布了 688 篇内容, 共 397.6 次阅读, 收获喜欢 1498 次。

关注

评论

发布
暂无评论
发现更多内容

电商秒杀系统设计(架构实战营 毕业设计项目)

Gor

golang实战之flag包

程序员欣宸

golang 8月月更

企业分账如何帮助用户解决成本优化和预算分配的问题

阿里巴巴中间件

阿里云 Serverless 云原生 SAE

kubernetes镜像构建和扫描

CTO技术共享

【算法实践】| 手把手带你实现快速排序算法

迷彩

快速排序 算法实践 8月月更

Latex安装教程(附美赛论文latex模板)

乌龟哥哥

8月月更

软件测试中的树莓酱定律

BY林子

软件测试 敏捷测试 质量内建

【Java】:二维数组的定义、初始化、长度以及循环遍历等...

翼同学

Java 学习 编程语言 分享 8月月更

架构实战营 毕业总结

Gor

头脑风暴:最大子序和

HelloWorld杰少

数据结构 算法 LeetCode 8月月更

SpringBoot 整合 Junit

springboot junit5 8月月更

《博弈论》— 人生何处不博弈

蔡农曰

读书笔记 博弈论

RocketMQ高可用设计之消息重试机制

急需上岸的小谢

8月月更

C++多态之析构和纯虚析构分析与示例(三)

CtrlX

c++ 后端 面向对象思想 8月月更

基于STM32L431设计的云端绿化管理系统(ESP8266+腾讯物联网云平台)

DS小龙哥

8月月更

Excelize 发布 2.6.1 版本更新,支持工作簿加密保护

xuri

开源 数据分析 Go 语言 Excelize Excel工具

一对一语音直播系统源码——如何解决音视频直播技术难点

开源直播系统源码

软件开发 直播系统源码 语音直播系统源码 语音直播源码 一对一语音直播

【Gopher 学个函数】边学边练,简单为 Go 上个分

梦想橡皮擦

Python 爬虫 8月月更

Curve Testing Camp Time|万元大奖等你来拿!

张慧

分布式存储 curve

为什么说:被观察者是 push 数据,迭代者是 pull 数据?

掘金安东尼

前端 函数式编程 8月月更

leetcode 560. Subarray Sum Equals K 和为 K 的子数组(中等)

okokabcd

LeetCode 算法与数据结构

P6项目管理系统的优缺点是什么?

PingCode

项目管理 项目管理软件

[JS真好玩] 遇到表格,手动翻页太麻烦?我教你写脚本,一页展示所有数据

HullQin

CSS JavaScript html 前端 8月月更

程序员最容易读错的单词,听到status我炸了

艾小仙

Java 前端

Teambition是什么软件?优缺点是什么?

PingCode

项目协作工具

数据治理(六):编译Atlas安装包

Lansonli

数据治理 8月月更

阿里云林小平:如何实现应用的持续发布?

阿里云弹性计算

持续发布 应用 自动化运维

Kubernetes中API安全加固

CTO技术共享

最新版MySQL8 绝对有用的lag函数实现同比、环比、均差计算

知识浅谈

MySQL 8月月更

机器学习服务文本翻译能力升级,中文直译模型让译文表达更地道!

HMS Core

动态尺寸模型优化实践之Shape Constraint IR Part II

阿里云大数据AI技术

深度学习 编译器 优化

你并不总希望在真正浏览器中进行测试:Selenium来营救_大前端_Jeanne Boyarsky_InfoQ精选文章