干货 | 解决 App 自动化测试的常见痛点(弹框及首页启动加载完成判断处理)

简介: 干货 | 解决 App 自动化测试的常见痛点(弹框及首页启动加载完成判断处理)

App 自动化测试中有些常见痛点问题,如果框架不能很好的处理,就可能出现元素定位超时找不到的情况,自动化也就被打断终止了。很容易打消做自动化的热情,导致从入门到放弃。比如下面的两个问题:

一是 App 启动加载时间较久(可能 App 本身加载慢,可能移动设备本身加载应用速度慢,也可能首页广告时间较长)。

另一个是各种弹框的出现,广告弹框,升级弹框,评价弹框等。在框架中如果不能处理好上面的情况,

以雪球 App 出现的几种弹框举例:

弹框一:

弹框二:

弹框三:

  • 弹框的影响范围
  • 弹框对我们自动化的影响主要是用例执行的打断,而至于弹框中广告内容的跳转或评价信息填写等属于另外的测试,因此我们主要是要将弹框处理消失,使应用回到用例执行的 PO;
  • 弹框的消失方式
  • 观察弹框,我们会发现一般为了保证用户体验,弹框都会方便用户进行一键消除,例如上述中雪球的各种弹框,可能点击一个叉号,可能任意点击其他地方,或者评价框这种直接点击“下次再说”等。

  • 弹框的处理效果
  • 自动化执行的任何时候,任意的弹框都可能出现,在这个时候用例不能失败,需要将对应的弹框正确处理后继续执行原用例,原用例的执行过程不受影响。
  1. 将需要处理的弹框元素加入到一个黑名单List中,遍历List,通过findElements方法得到的List大小来判断弹框元素是否存在,存在即点击处理
public static void handleAlert(){
        List<By> alertBox = new ArrayList<>();
        alertBox.add(By.id("ib_close"));   //广告弹框
        alertBox.add(By.id("md_buttonDefaultNegative")); //评价弹框
        alertBox.forEach(alert->{
            By adsLocator = alert;
            List<WebElement> ads = driver.findElements(adsLocator);
            if (ads.size() >= 1) {
                ads.get(0).click();
            }
        });
    }
  1. 将handleAlert()方法加到driver.findElement方法之前,使定位前先判断弹框的存在与否并进行处理
public static WebElement findElement(By by) {
            System.out.println(by);
            handleAlert();
            return driver.findElement(by);
            }

上述方法初步解决了弹框问题,但是缺点也很明显。

缺点:每次定位元素前都需要处理弹框,影响执行效率,速度较慢 因此我们引入try catch来解决此问题

我们利用try catch的异常捕获处理的机制,让元素仅在定位失败时才进入弹框处理handleAlert()方法,处理完毕后重新返回driver.findElement(by),对原case元素继续进行定位执行;这样就大大提升了处理效率,使处理更为精准。

public static WebElement findElement(By by) {
        try {
            System.out.println(by);
            return driver.findElement(by);
        } catch (Exception e) {
            System.out.println("进入弹框处理");
            handleAlert();
                return driver.findElement(by); 
            }
    }

递归处理:

一般情况下我们一次只会出现一个弹框,但是例外的是可能有一个以上的弹框同时出现,这样的话虽然处理了其中一个弹框,但是剩下的弹框依然会阻断用例的正常执行,这个时候就可以使用递归的方法,在处理完弹框后返回findElement方法自身,继续进行try catch,使之进入弹框处理逻辑

public static WebElement findElement(By by) {
          try {
              System.out.println(by);
              return driver.findElement(by);
          } catch (Exception e) {
              System.out.println("进入弹框处理");
              handleAlert();
                  return findElement(by); 
              }
      }

注意:

使用递归方法后有一个问题,就是假如并不是因为某个弹框的出现而导致的定位失败,而这个时候通过try catch进入到弹框处理逻辑后,由于并未匹配到弹框元素,所以递归就会进入一个死循环,不断重复着弹框处理的逻辑,所以使用递归时我们也需要对其次数进行限制;一般两个弹框同时出现已经算多的了,所以建议可以将递归的次数限制到最多两次便退出。

static int i = 1;
public static WebElement findElement(By by) {
    try {
        System.out.println(by);
        return driver.findElement(by);
    } catch (Exception e) {
        if (i > 2){   //设置最多递归两次
            i = 1;
            return driver.findElement(by);
        }
        System.out.println("进入弹框处理第"+i+"次");
        handleAlert();
        i++;
        return findElement(by); //最后调用自身完成递归,防止多弹框同时出现造成定位失败
        }
}

按照上面的方法,看似已经很好的解决了弹框的处理,但是可以注意到的是:

  • 在检查弹框的时候依然使用的是appium的定位,在当前页面中根据元素的属性去一一查找定位
  • 所有的黑名单中的弹框都会被定位查找一遍

而我们实际中最想要的也是最有效率的方法应该是:

  • 只有在当前页面中存在的弹框才对其进行定位、操作、处理。为了达到我们想要的效果,就需要借助于PageSource了。


1)appium的driver提供了一个getPageSource方法,此方法可以在当前页面可以得到一个文本字符串,也可以理解为当前页面的xml,我们利用这种xml文本来进行判断,就比用appium一一定位的方式要快速和精准的多了

String pageSource = driver.getPageSource();

2)设置黑名单,黑名单要使用元素的xpath,用来和PageSource文本做匹配,判断此弹框是否存在当前页面

String adBox = "com.xueqiu.android:id/ib_close";
String gesturePromptBox = "com.xueqiu.android:id/snb_tip_text";
String evaluateBox = "com.xueqiu.android:id/md_buttonDefaultNegative";
HashMap<String,By> map = new HashMap<>();
map.put(adBox,By.id("ib_close"));
map.put(gesturePromptBox,By.id("snb_tip_text"));
map.put(evaluateBox,By.id("md_buttonDefaultNegative"));

4)遍历map,判断黑名单弹框元素是否存在于当前pageSource,存在即根据弹框处理方式进行点击或其他操作(如上述中的新功能提示弹框,点击弹框自身无法消除,需点击页面其余部分方可消除)处理

map.entrySet().forEach(entry ->{
    if (pageSource.contains(entry.getKey())){
        if (entry.getKey().equals("com.xueqiu.android:id/snb_tip_text")){
            System.out.println("gesturePromptBox found");
            Dimension size = driver.manage().window().getSize();
            //点击屏幕的中心位置,消除新功能提示弹框
            new TouchAction<>(driver).tap(PointOption.point(size.width/2,size.height/2)).perform();
        }else {
          //其余弹框直接点击消除
            driver.findElement(entry.getValue()).click();
        }
    }
});
//很多弹框的话,最好的是直接定位到到底哪个弹框在界面上,元素的判断使用xpath
    public static void handleAlertByPageSource(){
        String pageSource = driver.getPageSource();//可以得到一个文本字符串,也可以理解为当前页面的xml
        //黑名单
        String adBox = "com.xueqiu.android:id/ib_close";
        String gesturePromptBox = "com.xueqiu.android:id/snb_tip_text";
        String evaluateBox = "com.xueqiu.android:id/md_buttonDefaultNegative";
        //将标记和定位符存入map
        HashMap<String,By> map = new HashMap<>();
        map.put(adBox,By.id("ib_close"));
        map.put(gesturePromptBox,By.id("snb_tip_text"));
        map.put(evaluateBox,By.id("md_buttonDefaultNegative"));
        //临时修改隐式等待时间,防止查找黑名单中元素过久
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        //遍历map,判断黑名单弹框元素是否存在于当前pageSource,存在即点击处理
        map.entrySet().forEach(entry ->{
            if (pageSource.contains(entry.getKey())){
                if (entry.getKey().equals("com.xueqiu.android:id/snb_tip_text")){
                    System.out.println("gesturePromptBox found");
                    Dimension size = driver.manage().window().getSize();
                    new TouchAction<>(driver).tap(PointOption.point(size.width/2,size.height/2)).perform();
                }else {
                    driver.findElement(entry.getValue()).click();
                }
            }
        });
        //判断完成后将隐式等待时间恢复
        driver.manage().timeouts().implicitlyWait(8,TimeUnit.SECONDS);
    }

6)最后将findElement方法中的handleAlert方法替换为handleAlertByPageSource方法即可

static int i = 1;
public static WebElement findElement(By by) {
    try {
        System.out.println(by);
        return driver.findElement(by);
    } catch (Exception e) {
        if (i > 2){   //设置最多递归两次
            i = 1;
            return driver.findElement(by);
        }
        System.out.println("进入弹框处理第"+i+"次");
                handleAlertByPageSource();
        i++;
        return findElement(by); //最后调用自身完成递归,防止多弹框同时出现造成定位失败
        }
}

再来解决首页加载时可能出现的坑。

App 启动加载时间较久(可能 App 本身加载慢,也可能是移动设备本身加载应用速度慢,也可能首页广告时间较长),导致定位超时,用例失败。对此我们又如下两步解决办法。

如标题所述,对首页进入使用显示等待,利用搜索控件的出现来判断是否进入了首页,这样不影响其他元素隐式等待的时间,也解决了首页初始化加载时间过长的问题。

例如雪球仅在进入首页后会出现 id为user_profile_container的用户信息控件,那么我们就可以以此为依据来判断应用是否加载完成进入了首页。

在启动方法中加入显示等待上述首页控件 30 秒,到控件可被定位时确认进入首页。

new WebDriverWait(driver,30)
                .until(ExpectedConditions.visibilityOfElementLocated(By.id("user_profile_container")));

缺点:

但是这样有个情况不能解决:若加载完成后有弹框出现,可能就一直无法定位到首页元素,但是实际上已经加载完成,比如下图的首页广告弹框 。

文章第二部分介绍了利用 PageSource 来判断弹框是否存在的方法,在这里依然适用,还是熟悉的味道,还是同样的套路,将弹框元素 xpath 也加入 PageSource 判断,这样无论首页控件和首页弹框哪一个被发现,就都可以判断应用已经加载完成,成功进入首页,剩下的就可以交给用例和其他处理逻辑了

new WebDriverWait(driver,30)
                .until(x ->{
                    String xml = driver.getPageSource();
                    Boolean checkResult = xml.contains("user_profile_container") || xml.contains("com.xueqiu.android:id/ib_close");
                    System.out.println("主页元素查找的结果是:" + checkResult);
                    return checkResult;
                });

好了,经过上面的分析之后,我们终于搞定了入门 APP 自动化测试时的老大难问题。搞定了弹框及首页启动时加载完成如何判断处理。

技能学习,站在高手/过来人的肩膀上,才能高效进阶。

相关文章
|
7月前
|
测试技术 UED Python
App自动化测试:高级控件交互技巧
Appium 的 Actions 类支持在移动应用自动化测试中模拟用户手势,如滑动、长按等,增强交互性测试。ActionChains 是 Selenium 的概念,用于网页交互,而 Actions 专注于移动端。在Python中,通过ActionChains和W3C Actions可以定义手势路径,例如在手势解锁场景中,先点击设置,然后定义触点移动路径执行滑动解锁,最后验证解锁后的元素状态。此功能对于确保应用在复杂交互下的稳定性至关重要。
|
22天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
64 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
2月前
|
测试技术
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
本文是关于自动化测试项目实战笔记,主要介绍了如何测试用户注册功能,包括验证码错误、注册成功以及弹框处理的测试步骤和代码实现。
97 2
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
|
2月前
|
测试技术 数据安全/隐私保护 Python
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
本文介绍了使用Selenium进行自动化测试时如何测试用户登录的不同情况,包括账号密码错误、登录成功以及处理登录时出现的弹框,并提供了相应的Python代码实现。
66 0
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
|
2月前
|
测试技术 Python
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法
本文介绍了使用Python的unittest框架来加载测试用例的四种方法,包括通过测试用例类、模块、路径和逐条加载测试用例。
74 0
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法
|
3月前
|
测试技术
基于LangChain手工测试用例转App自动化测试生成工具
在传统App自动化测试中,测试工程师需手动将功能测试用例转化为自动化用例。市面上多数产品通过录制操作生成测试用例,但可维护性差。本文探讨了利用大模型直接生成自动化测试用例的可能性,介绍了如何使用LangChain将功能测试用例转换为App自动化测试用例,大幅节省人力与资源。通过封装App底层工具并与大模型结合,记录执行步骤并生成自动化测试代码,最终实现高效自动化的测试流程。
|
4月前
|
Java 测试技术 容器
从零到英雄:Struts 2 最佳实践——你的Web应用开发超级变身指南!
【8月更文挑战第31天】《Struts 2 最佳实践:从设计到部署的全流程指南》深入介绍如何利用 Struts 2 框架从项目设计到部署的全流程。从初始化配置到采用 MVC 设计模式,再到性能优化与测试,本书详细讲解了如何构建高效、稳定的 Web 应用。通过最佳实践和代码示例,帮助读者掌握 Struts 2 的核心功能,并确保应用的安全性和可维护性。无论是在项目初期还是后期运维,本书都是不可或缺的参考指南。
51 0
|
4月前
|
SQL 存储 数据管理
掌握SQL Server Integration Services (SSIS)精髓:从零开始构建自动化数据提取、转换与加载(ETL)流程,实现高效数据迁移与集成——轻松上手SSIS打造企业级数据管理利器
【8月更文挑战第31天】SQL Server Integration Services (SSIS) 是 Microsoft 提供的企业级数据集成平台,用于高效完成数据提取、转换和加载(ETL)任务。本文通过简单示例介绍 SSIS 的基本使用方法,包括创建数据包、配置数据源与目标以及自动化执行流程。首先确保安装了 SQL Server Data Tools (SSDT),然后在 Visual Studio 中创建新的 SSIS 项目,通过添加控制流和数据流组件,实现从 CSV 文件到 SQL Server 数据库的数据迁移。
239 0
|
7月前
|
安全 开发工具 Android开发
3种方式自动化控制APP
自动化控制APP不管是在工作还是生活方面,都可以帮助我们高效地完成任务,节省时间和精力。本文主要介绍自动化控制APP的3种常用方式。
3种方式自动化控制APP
|
7月前
|
XML 测试技术 数据格式
解决 App 自动化测试的常见痛点
在App自动化测试中,常见挑战包括启动加载慢和弹框干扰。为处理弹框,可以创建一个黑名单列表,遍历并点击消除。使用`handleAlertByPageSource()`方法结合`getPageSource()`判断弹框元素在当前页面的存在性,提高效率。对于首页加载延迟,使用显示等待特定元素如`user_profile_container`,但需注意弹框可能阻止元素定位。因此,结合PageSource判断首页元素和弹框,确保加载完成判断的准确性。通过这样的优化,能更有效地处理自动化测试中的中断问题。