| 場景類型 | 典型業務 | 測試策略 |
| 純數據回填 | 選人、選部門、選業務類型,僅回填父頁面字段 | 數據預置法:測試代碼直接構造返回數據,通過JS賦值父頁面字段,完全跳過模態框 |
| 觸發後續業務邏輯 | 工作流派發、審批流轉,返回值後仍需執行一系列JS操作 | JS覆蓋法(Hack):攔截showModalDialog,首次改為window.open操作,二次調用時直接返回預置數據 |
核心Hack方案:雙階段JS攔截注入
階段一:首次點擊 → 將模態框轉為普通窗口操作
public void clickAndSelectModalDialog(String locator) {
clickForModalDialog(locator);
selenium.selectWindow("name=modal");
}
private void clickForModalDialog(String locator) {
// 構造JS:覆蓋父頁面showModalDialog為window.open
String overrideJs =
"if (selenium.browserbot.getCurrentWindow().showModalDialog) {" +
" selenium.browserbot.getCurrentWindow().showModalDialog = " +
" function(sURL, vArguments, sFeatures) {" +
" return selenium.browserbot.getCurrentWindow().open(sURL, 'modal', sFeatures);" +
" };" +
"}";
selenium.getEval(overrideJs); // 注入覆蓋
selenium.click(locator); // 觸發點擊,實際打開window.open
selenium.openWindow("", "modal"); // 同步窗口句柄
selenium.waitForPopUp("modal", "15000"); // 等待彈窗加載
}
階段二:在彈窗操作完成 → 關閉彈窗 → 二次點擊注入返回值
public void acceptModalValue(String locator, String[] values) {
String overrideJs =
"if (selenium.browserbot.getCurrentWindow().showModalDialog) {" +
" selenium.browserbot.getCurrentWindow().showModalDialog = " +
" function(sURL, vArguments, sFeatures) {" +
generateModalDialogReturnObject(values) + // 生成返回對象代碼
" return temp;" +
" };" +
"}";
selenium.getEval(overrideJs);
selenium.click(locator); // 二次點擊,此次直接獲得返回值繼續執行後續JS
}
private String generateModalDialogReturnObject(String[] values) {
StringBuffer sb = new StringBuffer("var temp=new Array();");
for (int i = 0; i < values.length; i++) {
sb.append("temp[").append(i).append("]='").append(values[i]).append("';");
}
return sb.toString();
}
完整測試用例:部門選擇模態框端到端驗證
// 1. 點擊部門選擇圖標,進入階段一
String depChooseLocator = ".../img";
clickAndSelectModalDialog(depChooseLocator);
// 2. 在彈窗中操作部門樹(普通Selenium操作)
// ... 選中節點、展開樹等 ...
// 3. 獲取彈窗準備返回的數據
String name = selenium.getEval("window.seltree.GetAllCheckText();");
String id = selenium.getEval("window.seltree.GetAllNodeId();");
// 4. 關閉彈窗,切回主窗口
selenium.getEval("window.close();"); // 或點擊彈窗關閉按鈕
selenium.selectWindow("name=main95598");
// 5. 組裝返回值,執行階段二
String[] returnValues = {name, id};
acceptModalValue(depChooseLocator, returnValues);
// 6. 驗證父頁面字段已正確回填
Assert.assertEquals(name, selenium.getValue("departmentNameField"));
Assert.assertEquals(id, selenium.getValue("departmentIdField"));
關鍵技術細節與避坑指南
1. 窗口句柄管理
- 首次
openWindow("", "modal")建立Selenium內部窗口映射
selectWindow("name=modal")切換上下文至彈窗
- 操作完成後
selectWindow("name=主窗口名")切回,主窗口名需提前獲取(如selenium.getEval("window.name"))
2. 時序控制
waitForPopUp超時時間需覆蓋頁面加載最長耗時
- 兩次
click(locator)之間必須完成彈窗操作並關閉,否則第二次點擊可能被瀏覽器攔截或觸發異常邏輯
3. 返回值結構約定
模態框返回值通常為數組[顯示文本, 隱藏ID, ...],父頁面原有JS邏輯按索引解析。測試時必須嚴格保持相同順序與類型,否則後續業務JS會報錯。
4. 瀏覽器兼容性
- 方案依賴
selenium.browserbot,僅適用Selenium 1.x (RC)架構
- Selenium 2/3/4 (WebDriver)架構下,
showModalDialog在現代瀏覽器(Edge/Chrome/Firefox)已棄用/移除,建議業務遷移至window.open+postMessage或模態組件庫(Bootstrap Modal等),測試難度大幅降低
模板化封裝建議
將上述邏輯封裝為ModalDialogHandler工具類,暴露模板方法:
public abstract class ModalDialogTestTemplate {
protected Selenium selenium;
public void executeModalDialogTest(String locator, ModalDialogAction action) {
// 階段一
clickAndSelectModalDialog(locator);
try {
// 業務方實現具體彈窗操作與數據提取
String[] returnValues = action.performInModal(selenium);
// 階段二
closeModalAndReturnMain();
acceptModalValue(locator, returnValues);
} finally {
ensureBackToMainWindow();
}
}
// ... 內部實現 clickAndSelectModalDialog / acceptModalValue 等
}
總結
針對showModalDialog這類阻塞式舊API,Selenium無法原生支持。通過JS運行時覆蓋將其轉化為可控的window.open流程,配合雙階段注入(首次轉彈窗操作、二次注入返回值),可實現端到端自動化驗證。但鑑於現代瀏覽器已淘汰該API,**根治之道仍是業務代碼重構**,測試層面的Hack僅作過渡方案。
- 上一篇文章: JSF實現Checkbox狀態持久化:頁面刷新後保持選中狀態的最佳實踐
- 下一篇文章: jBPM3入門實戰:基於MySQL的簡單報銷流程完整實現
Copyright ©
程式碼之家 All Rights Reserved