• 基于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 }    
  • 相关阅读:
    Node.js 0.12: 正确发送HTTP POST请求
    pm2 常用命令
    IntelliJ IDEA Configuring projects
    socket.io入门整理教程
    幂等函数
    Linux 下 ps 命令
    Linux 下 tail 命令
    Linux下chmod命令
    Linux下ll命令与ls -l
    Thrift——初学
  • 原文地址:https://www.cnblogs.com/Seven13G/p/7825957.html
Copyright © 2020-2023  润新知