Selenium 以真實瀏覽器驅動 Web 測試,區別於模擬 HTTP 請求的工具。結合 TestNG 強大的參數化、依賴管理、失敗重跑能力,再整合 DbUnit (資料庫狀態管理) 與 Cargo (容器生命週期自動化),即可構建完全自動化、可重複執行、跨瀏覽器的驗收測試體系。
[Ant/Maven/CI] → [Cargo: 啟動容器 + 部署 WAR] → [Selenium Server 啟動]
→ [TestNG: 執行 Selenium 測試套件 (Firefox/IE/Chrome)]
→ [DbUnit: 每測試群組前 CLEAN_INSERT 種子資料]
→ [Cargo: 停止容器] → [Selenium Server 關閉] → [測試報告]
Selenium driver = new DefaultSelenium(
"localhost", // Selenium Server 位址
SeleniumServer.getDefaultPort(), // 4444
"*firefox", // 瀏覽器啟動參數
"https://localhost:8080/app/" // 被測應用基礎 URL
);
driver.start();
// ... 執行測試步驟 ...
driver.stop();
| 方法 | 用途 | 範例 |
|---|---|---|
| open(url) | 導航至頁面 | driver.open("/CreateWidget.html") |
| type(locator, value) | 輸入框填值 | driver.type("widget", "book-01") |
| click(locator) | 點擊元素 | driver.click("submit") |
| select(locator, option) | 下拉選單選值 | driver.select("type", "book") |
| waitForPageToLoad(timeoutMs) | 等待頁面載入 | driver.waitForPageToLoad("10000") |
| getText(locator) | 取得元素文字 | driver.getText("success") |
| getTitle() | 取得頁面標題 | driver.getTitle() |
定位器支援:id=xxx、name=xxx、xpath=//div[@class='foo']、css=.bar、dom=...。
public class CreateWidgetUATest {
private Selenium driver;
@Parameters({"selen-svr-addr", "brwsr-path", "aut-addr"})
@BeforeClass
private void init(String selenSrvrAddr, String bpath, String appPath) throws Exception {
driver = new DefaultSelenium(selenSrvrAddr, SeleniumServer.getDefaultPort(), bpath, appPath);
driver.start();
}
@Parameters({"aut-addr"})
@Test
public void verifyCreate(String appPath) throws Exception {
driver.open(appPath + "/CreateWidget.html");
driver.type("widget", "book-01");
driver.select("type", "book");
driver.type("definition", "book widget type book");
driver.click("submit");
driver.waitForPageToLoad("10000");
assertEquals(driver.getText("success"), "The widget book-01 was successfully created.");
}
@Parameters({"aut-addr"})
@Test
public void verifyCreationError(String appPath) throws Exception {
driver.open(appPath + "/CreateWidget.html");
driver.type("widget", "book-02");
driver.select("type", "book");
driver.type("definition", ""); // 故意留空觸發驗證錯誤
driver.click("submit");
driver.waitForPageToLoad("10000");
assertEquals(driver.getText("failure"), "There was an error in creating the widget.");
}
@AfterClass
private void stop() throws Exception {
driver.stop();
}
}
<suite name="User Acceptance Tests" verbose="1" parallel="tests" thread-count="2">
<!-- 共用參數:DbUnit、Selenium Server、App URL -->
<parameter name="seed-path" value="test/conf/gt15-seed.xml"/>
<parameter name="db-driver" value="org.hsqldb.jdbcDriver"/>
<parameter name="db-url" value="jdbc:hsqldb:hsql://127.0.0.1"/>
<parameter name="db-user" value="sa"/>
<parameter name="db-psswrd" value=""/>
<parameter name="selen-svr-addr" value="localhost"/>
<parameter name="aut-addr" value="https://localhost:8080/gt15/"/>
<test name="GT15 CRUDs - Firefox">
<parameter name="brwsr-path" value="*firefox"/>
<packages>
<package name="test.com.acme.gt15.Web.selenium"/>
<package name="test.com.acme.gt15.Web.selenium.fixtures"/>
</packages>
</test>
<test name="GT15 CRUDs - IE">
<parameter name="brwsr-path" value="*iexplore"/>
<packages>
<package name="test.com.acme.gt15.Web.selenium"/>
<package name="test.com.acme.gt15.Web.selenium.fixtures"/>
</packages>
</test>
</suite>
關鍵特性:
parallel="tests" thread-count="2":Firefox 與 IE 並行執行@BeforeTest / @AfterTest:測試群組級 Fixture (DbUnit 種子資料)public class DatabaseFixture {
@Parameters({"seed-path", "db-driver", "db-url", "db-user", "db-psswrd"})
@BeforeTest
public void seedDatabase(String seedpath, String driver, String url, String user, String pssword) throws Exception {
IDatabaseConnection conn = getConnection(driver, url, user, pssword);
IDataSet data = getDataSet(seedpath);
try {
DatabaseOperation.CLEAN_INSERT.execute(conn, data); // 刪除舊資料 → 插入新資料
} finally {
conn.close();
}
}
private IDataSet getDataSet(String path) throws IOException, DataSetException {
return new FlatXmlDataSet(new File(path));
}
private IDatabaseConnection getConnection(String driver, String url, String user, String pssword) throws Exception {
Class.forName(driver);
Connection jdbcConnection = DriverManager.getConnection(url, user, pssword);
return new DatabaseConnection(jdbcConnection);
}
}
CLEAN_INSERT 確保每個測試群組 (
<target name="ua-test" depends="compile-tests,war">
<taskdef resource="cargo.tasks">
<classpath>
<pathelement location="${libdir}/${cargo-jar}"/>
<pathelement location="${libdir}/${cargo-ant-jar}"/>
</classpath>
</taskdef>
<!-- 1. Cargo 啟動 Tomcat 並部署 WAR -->
<cargo containerId="tomcat5x" action="start" wait="false" id="${tomcat-refid}">
<zipurlinstaller installurl="${tomcat-installer-url}"/>
<configuration type="standalone" home="${tomcatdir}">
<property name="cargo.remote.username" value="admin"/>
<property name="cargo.remote.password" value=""/>
<deployable type="war" file="${wardir}/${warfile}"/>
</configuration>
</cargo>
<!-- 2. 啟動 Selenium Server 並執行測試 -->
<antcall target="_start-selenium"/>
<!-- 3. 測試完成後停止 Tomcat -->
<cargo containerId="tomcat5x" action="stop" refid="${tomcat-refid}"/>
</target>
<target name="_start-selenium">
<java jar="${libdir}/${selenium-srvr-jar}" fork="true" spawn="true"/>
<antcall target="_run-ua-tests"/>
<get dest="${testreportdir}/results.txt"
src="${selenium-srvr-loc}/selenium-server/driver/?cmd=shutDown"/>
</target>
<target name="_run-ua-tests">
<taskdef classpathref="build.classpath" resource="testngtasks"/>
<testng outputDir="${testreportdir}" classpath="${testclassesdir};${classesdir}" haltonfailure="true">
<xmlfileset dir="./test/conf" includes="testng.xml"/>
<classpath><path refid="build.classpath"/></classpath>
</testng>
</target>
Cargo 優勢:同一 API 支援 Tomcat/JBoss/Jetty/GlassFish/Resin,自動下載安裝指定版本,跳過已存在版本。
| 舊技術 | 現代替代 | 遷移重點 |
|---|---|---|
| Selenium RC (DefaultSelenium) | Selenium 4 WebDriver | WebDriver API、W3C 標準、原生瀏覽器驅動、無需 Selenium Server (可選 Grid) |
| Ant 建置腳本 | Maven / Gradle | 依賴管理、標準目錄結構、插件生態豐富 |
| Cargo Ant Task | Cargo Maven Plugin / Testcontainers | Testcontainers 以 Docker 管理容器,更輕量、隔離性更好 |
| DbUnit XML 種子檔 | Testcontainers + Flyway/Liquibase / @Sql | 資料庫遷移工具管理結構,測試資料用 @Sql 或程式化建構 |
| TestNG XML Suite | TestNG 7+ / JUnit 5 | JUnit 5 擴展模型強大,TestNG 仍優於參數化/依賴/並行控制 |
| IE/Firefox 參數化 | Chrome/Edge/Firefox/Safari + Selenium Grid / Cloud (Sauce Labs/BrowserStack) | 跨瀏覽器雲端測試平台 |
Selenium + TestNG + DbUnit + Cargo 組合展示了端到端驗收測試自動化的完整樣貌:
雖技術棧已演進,但架構思維 (基礎設施即代碼、測試隔離、參數化、可重複性) 永不過時。現代專案建議採用 WebDriver + Testcontainers + JUnit 5/TestNG + GitHub Actions/GitLab CI 重構同樣架構。