Sha*_*ntu 5 java selenium selenium-webdriver
我正在使用 Selenium Webdriver 自动化一个网站(填写表格和点击),以节省我的用户的时间。不过我遇到了一个烦人的问题:
Selenium 似乎不支持浏览器本身的任何事件侦听器。当关闭浏览器
driver.quit()
时不叫 和不能使用的驱动器保持其抛出各种异常。无法知道浏览器何时关闭,我无法创建新的驱动程序实例。
我需要的是在浏览器关闭时通知我的程序的某种方式。
用户可能会出于破坏我的程序的任何原因关闭浏览器。用户不能在不重新启动应用程序的情况下再次运行自动化任务。driver.quit()
如果用户想再次运行它,知道浏览器何时关闭将允许我调用并创建一个新的驱动程序实例。
当浏览器死机时,抛出的错误在浏览器之间并不统一,这一事实使问题变得更加复杂。使用 Firefox,我可能会遇到 UnreachableBrowserException,以及 Chrome NullPointer 和 WebDriverExceptions。
澄清一下,我知道如何关闭驱动程序和浏览器,但我不知道它们何时被外部资源关闭。这可以在 Selenium 2 中以跨浏览器的方式完成(如果是,如何)还是我需要找到另一种方式(如另一个库)来观看浏览器窗口?
我在 JNA (Java Native Access) 3.4 的帮助下解决了这个问题,它已经包含在 Selenium 中。我的目标平台只是 Windows,但应该不需要做太多工作就可以实现跨平台。我必须执行以下操作:
tasklist
在启动 WebDriver 之前收集所有浏览器进程 ID(这可以使用诸如或 powershell 之类的实用程序来完成Get-Process
)。如果您确定在启动应用程序之前不会运行浏览器进程,则可以省略此步骤。Kernel32.INSTANCE.OpenProcess
创建对象。HANDLE
Kernel32.INSTANCE.WaitForMultipleObjects
方法的信号。这是我使用的代码,希望对其他人有帮助:
public void startAutomation() throws IOException {
Set<Integer> pidsBefore = getBrowserPIDs(browserType);
automator.initDriver(browserType); //calls new ChromeDriver() for example
Set<Integer> pidsAfter = getBrowserPIDs(browserType);
pidsAfter.removeAll(pidsBefore);
ProcessGroupExitWatcher watcher = new ProcessGroupExitWatcher(pidsAfter);
watcher.addProcessExitListener(new ProcessExitListener() {
@Override
public void processFinished() {
if (automator != null) {
automator.closeDriver(); //calls driver.quit()
automator = null;
}
}
});
watcher.start();
//do webdriver stuff
}
private Set<Integer> getBrowserPIDs(String browserType) throws IOException {
Set<Integer> processIds = new HashSet<Integer>();
//powershell was convenient, tasklist is probably safer but requires more parsing
String cmd = "powershell get-process " + browserType + " | foreach { $_.id }";
Process processes = Runtime.getRuntime().exec(cmd);
processes.getOutputStream().close(); //otherwise powershell hangs
BufferedReader input = new BufferedReader(new InputStreamReader(processes.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
processIds.add(Integer.parseInt(line));
}
input.close();
return processIds;
}
Run Code Online (Sandbox Code Playgroud)
以及观察者的代码:
/**
* Takes a <code>Set</code> of Process IDs and notifies all listeners when all
* of them have exited.<br>
*/
public class ProcessGroupExitWatcher extends Thread {
private List<HANDLE> processHandles;
private List<ProcessExitListener> listeners = new ArrayList<ProcessExitListener>();
/**
* Initializes the object and takes a set of pids and creates a list of
* <CODE>HANDLE</CODE>s from them.
*
* @param processIds
* process id numbers of the processes to watch.
* @see HANDLE
*/
public ProcessGroupExitWatcher(Set<Integer> processIds) {
processHandles = new ArrayList<HANDLE>(processIds.size());
//create Handles from the process ids
for (Integer pid : processIds) {
processHandles.add(Kernel32.INSTANCE.OpenProcess(Kernel32.SYNCHRONIZE, false, pid)); //synchronize must be used
}
}
public void run() {
//blocks the thread until all handles are signaled
Kernel32.INSTANCE.WaitForMultipleObjects(processHandles.size(), processHandles.toArray(new HANDLE[processHandles.size()]), true,
Kernel32.INFINITE);
for (ProcessExitListener listener : listeners) {
listener.processFinished();
}
}
/**
* Adds the listener to the list of listeners who will be notified when all
* processes have exited.
*
* @param listener
*/
public void addProcessExitListener(ProcessExitListener listener) {
listeners.add(listener);
}
/**
* Removes the listener.
*
* @param listener
*/
public void removeProcessExitListener(ProcessExitListener listener) {
listeners.remove(listener);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:以这种方式使用 powershell 时,将执行用户的配置文件脚本。这可能会产生意外的输出,从而破坏上述代码。因此,更推荐使用任务列表。
归档时间: |
|
查看次数: |
3522 次 |
最近记录: |