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 8和Java 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 8 和Java 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