基于pywinauto的Windows平台上自动化测试实践(2)

一、利用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 }
其中,在脚本执行执行完后调用sleep()让主线程睡眠一秒,否则会导致清理线程可能会还没拿到缓冲区数据就被end标识符结束。

 

三、最终结果

下面就是整个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 }
View Code

需要注意的是,在调用本地应用程序的过程中网站通常会利用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 }    
原文地址:https://www.cnblogs.com/Seven13G/p/7825957.html