一、利用Jython进行Java和Python的数据交互的局限
在上一节中,利用Jython实现了Java对Python脚本的简单调用,但是jython仅能支持标准的python代码,不支持python扩展包。因而在Java调用的过程中也就堵死了Pywinauto的利用,因此我们需要寻找另一种调用方式。
二、利用Java.lang中的Runtime类调用Python脚本
此方法实际上是利用Runtime类新开进程,来独立调用Python脚本。下面是网上找到的例子:
1 public String python(String pythonPath, String[] params) { 2 File file = new File(pythonPath); 3 if (!file.exists()){ 4 return "python脚本不存在!"; 5 } 6 7 String[] command = Arrays.copyOf(new String[]{"python", pythonPath}, params.length + 2); 8 System.arraycopy(params, 0, command, 2, params.length); 9 10 List<String> res = new ArrayList<>(); 11 try { 12 Process process = Runtime.getRuntime().exec(command, null, null); 13 process.waitFor(); 14 15 Scanner scanner = new Scanner(process.getInputStream()); 16 while (scanner.hasNextLine()) { 17 String line = scanner.nextLine(); 18 res.add(line); 19 } 20 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 27 return "success"; 28 }
例子中,参数pythonPath就是python脚本的绝对路径,参数params是脚本的参数数组,command就是执行python命令字符串数组,格式就是python + 脚本 + 参数,构造好command后传入exec()中执行新进程,然后调用waitFor()函数等待进程结束,结束后从进程的输入流中获得脚本的输出结果存储到字符串数组中。
乍一看,上面的代码并没有问题,对于少量的输出结果执行后相当完美,但是当脚本的输出结果大小大于inputStream缓冲区大小时,程序会阻塞在waitFor()函数这里,问题就在于脚本的输出结果是在进程执行完之后才读取,一个好的解决办法就是新开一个清理线程来不断清空缓冲区,也就是输出和读取同时进行,代码如下:
1 public String python(String pythonPath, String[] params) { 2 File file = new File(pythonPath); 3 if (!file.exists()){ 4 return "python脚本不存在!"; 5 } 6 7 String[] command = Arrays.copyOf(new String[]{"python", pythonPath}, params.length + 2); 8 System.arraycopy(params, 0, command, 2, params.length); 9 10 List<String> res = new ArrayList<>(); 11 try { 12 Process process = Runtime.getRuntime().exec(command, null, null); 13 14 ClearThread ct = new ClearThread(process); 15 ct.start(); 16 17 process.waitFor(); 18 Thread.sleep(1000); 19 20 ct.setEnd(true); 21 res = ct.getRes(); 22 } catch (IOException e) { 23 e.printStackTrace(); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 28 return "success"; 29 } 30 31 class ClearThread extends Thread { 32 Process process; 33 boolean end; 34 List<String> res; 35 36 public ClearThread(Process process) { 37 this.process = process; 38 end = false; 39 res = new ArrayList<>(); 40 } 41 42 @Override 43 public void run() { 44 if (process == null) { 45 return; 46 } 47 48 Scanner scanner = new Scanner(process.getInputStream()); 49 while (process != null && !end) { 50 while (scanner.hasNextLine()) { 51 String line = scanner.nextLine(); 52 res.add(line); 53 } 54 } 55 } 56 57 public void setEnd(boolean end) { 58 this.end = end; 59 } 60 61 public List<String> getRes() { 62 return res; 63 } 64 }
三、最终结果
下面就是整个case的具体步骤,也就是最终的完整版本,利用Selenium + Robot 实现从web调用本地应用程序,而后利用Pywinauto实现Windows程序的自动化运行,再将结果反馈到Selenium主程序,进行验证或其他操作。
1 @Test 2 public void a_deviceUserActivity() { 3 boolean result = false; 4 DeviceDetailPage deviceDetailPage = nvBar.openSitesManaged(test).openDevicesDetail("CH-AEM-2012-01"); 5 deviceDetailPage.openRemoteTakeoverPage(); 6 super.sleep(20); 7 String[] parameter = {}; 8 List<String> returnData = this.python("C:\Selenium\AEMAgentFramework\aem\framework\Pywin.py", parameter); 9 for(String s : returnData){ 10 System.out.println(s); 11 } 12 super.sleep(30); 13 deviceDetailPage.refreshPage(); 14 if(returnData.size()==3){ 15 result = deviceDetailPage.validateRemoteTakeoverActivity(Constants.getEnvDetails().get("username"), 16 returnData.get(0).substring(3), returnData.get(1).substring(3)); 17 } 18 if(result) 19 reportPass("CEN-4732 User Acitvity - Remote Takeover function is working"); 20 else 21 reportFailure("CEN-4732 User Acitvity - Remote Takeover is failed"); 22 23 Assert.assertTrue(result, "CEN-4732 User Acitvity - Remote Takeover is failed"); 24 }
需要注意的是,在调用本地应用程序的过程中网站通常会利用URI Schema这种方式打开本地应用程序,然而Selenium对这种类似Alert的弹框并不支持,因此需要利用Java的Robot工具实现点击操作
1 Robot robot; 2 try { 3 robot = new Robot(); 4 robot.keyPress(java.awt.event.KeyEvent.VK_TAB); 5 robot.keyPress(java.awt.event.KeyEvent.VK_TAB); 6 robot.keyPress(java.awt.event.KeyEvent.VK_ENTER); 7 } catch (AWTException e) { 8 e.printStackTrace(); 9 }