扩展Playwright自动等待方法

问题

Playwright本身自带了非常不错的自动等待机制,在 page.click(selector) page.fill(selector, value) 之类的元素操作会自动等待元素可见且可操作。但是在项目上进行应用的时候,还是会出现这样那样的问题,比如:

  1. 页面跳转后的页面操作,可能会出现错误:
playwright._impl._api_types.Error: Execution context was destroyed, most likely because of a navigation.
  1. 页面跳转后点击第一个按钮,Playwright已经发出点击,但是实际页面元素由于页面加载等原因没有接收到,导致点击操作没有生效,后续步骤无法进行。这种问题比较隐蔽,报错不固定,调试也比较困难,如点击该按钮应该要弹出新页面,结果报错如下:
playwright._impl._api_types.TimeoutError: Timeout while waiting for event "page"
  1. 页面中有iframe嵌套的情况时,如果没有iframe加载出来或者发生跳转时,会出现定位不到frame而直接返回None,导致报错:
AttributeError: 'NoneType' object has no attribute 'fill'

尝试使用Playwright自带的机制解决

  1. 以上三个问题都可以使用 page.wait_for_timeout(<timeout>) 加入固定的等待时间进行处理,但是需要在所有上述情景中加入等待,而且由于是固定等待时间,时间的长短也不好控制,过短的话没有效果,过长的话又会导致自动化测试执行时间的延长,而且页面加载时间可能是随机的、依赖环境的,无法准确预知。所以一般来说,不建议使用固定等待时间来处理。
  2. 创建浏览器对象时加入 slow_mo 参数,这样会使Playwright的每一步操作前都等待固定的时间,优点是不需要在每一步操作前进行添加,一次配置,全局可用,缺点和上面一样,本质同样是固定等待时间,而且涉及每一步操作,会更加拖慢执行速度
  3. Playwright提供了 page.wait_for_load_state() 方法,支持3种参数 load domcontentloadednetworkidle ,可以等待页面加载至预期状态。但是经我测试发现这种方法并不是很好用,可以解决部分问题,但是还是有很大概率等待时间不足(即使我把三种参数都用上了)。

为了更优雅的解决这个问题,我就在Playwright的基础上进行了扩展

扩展Playwright

基本思路

必须抛弃掉固定等待时间的方法,即使用到固定等待时间,也需要在一个循环中判断达到某个条件(如元素出现)就退出循环。注意到Playwright提供了 page.on 注册回调函数的方法,那么就可以在回调函数中记录时间发生的时间,等待至一定时间内没有事件发生即为页面加载完毕(类似于networkidle )。

实现方法

# client.py
import time
from abc import ABC, abstractmethod

from playwright.sync_api import sync_playwright, Frame, Page

class Client(ABC):
    playwright = None
    browser = None

    def __init__(self, url: str):
        self.url = url
        self.context = None
        self.main_page = None
        self.last_busy_time = time.time()

    @abstractmethod
    def register_page(self):
        pass

    def _update_busy_time(self, event=None) -> None:
        if isinstance(event, Page) or isinstance(event, Frame):
            self._register_busy_time(event)
				self.last_busy_time = time.time()

    def _register_busy_time(self, obj) -> None:
        obj.on('domcontentloaded', self._update_busy_time)
        obj.on('download', self._update_busy_time)
        obj.on('filechooser', self._update_busy_time)
        obj.on('frameattached', self._update_busy_time)
        obj.on('framedetached', self._update_busy_time)
        obj.on('framenavigated', self._update_busy_time)
        obj.on('load', self._update_busy_time)
        obj.on('pageerror', self._update_busy_time)
        obj.on('popup', self._update_busy_time)
        obj.on('request', self._update_busy_time)
        obj.on('requestfailed', self._update_busy_time)
        obj.on('requestfinished', self._update_busy_time)
        obj.on('response', self._update_busy_time)

    def start(self) -> None:
        Client.playwright = sync_playwright().start()
        Client.browser = Client.playwright.chromium.launch()
        self.context = Client.browser.new_context()
        self.main_page = self.context.new_page()
        self._register_busy_time(self.main_page)
        self.main_page.goto(self.url)
        self.register_page()
# page.py
import time

class BasePage(object):
    def __init__(self, client, page=None):
        self.client = client
        if not page:
            self.page = client.main_page
        else:
            self.page = page

    def wait_until_idle(self, timeout=1) -> None:
        while time.time() - self.client.last_busy_time < timeout:
            self.page.wait_for_timeout(100)

以上为在之前展示过的Client-Page Object模式下的实现,只保留了核心方法。

代码解析

  1. Client 类中定义 last_busy_time 属性,用于记录最后一次页面事件发生的时间。
  2. Client 类中的 _update_busy_time 方法,用于在 page.on 中注册回调方法,更新last_busy_time ,并当事件为打开新页面或frame时,在新页面或frame中对事件注册 page.on 回调(这里比较简单,只判断了事件类型,实际应用时可以根据需要定制)。
  3. Client 类中的 _register_busy_time 方法,用于为页面事件注册回调函数(这里只是列举可能用到的事件类型,实际应根据项目特点进行定制)。
  4. start 方法中创建第一个页面后,调用_register_busy_time 方法,即可将后续所有打开的页面和frame都对页面事件进行注册,只要发生对应的页面事件就会更新last_busy_time 属性为当前时间。
  5. BasePage 类中定义了 wait_until_idle 方法,用于判断当页面空闲时间大于 timeout 时即停止等待,认为当前页面加载完毕,并且并且页面已经空闲的时候会即刻返回,不会增加测试执行时间。

总结

如上代码提供了一个自动等待页面空闲的方法,可以在任意需要等待的地方使用,使用效果优于等待固定时间。如果配合自己封装的 Element ,则可以在每一个操作前面加入此等待,这样就可以摆脱手动添加等待的烦恼;如果再配合重试机制,那么执行测试的稳定性将会更上一层楼。

  • 9
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Playwright是一个功能强大的自动化测试框架,用于在不同的浏览器(如Chrome、Firefox和Safari)上执行自动化操作。它提供了一套简洁的API,使得编写和维护自动化测试变得更加容易。以下是一些Playwright自动化框架的特点和优势: 1. 多浏览器支持:Playwright支持多种主流浏览器,包括Chrome、Firefox、Safari和Edge。你可以使用相同的代码在不同的浏览器上运行你的自动化测试。 2. 跨平台支持:Playwright可以在多个操作系统上运行,包括Windows、Mac和Linux。这使得团队可以在不同的开发环境中共享和运行测试。 3. 强大的自动化功能:Playwright提供了丰富的API来处理各种自动化操作,如点击、填写表单、截图、模拟用户行为等。它还支持异步操作和等待页面加载等待条件。 4. 可靠性和稳定性:Playwright具有稳定性和可靠性,可以处理各种复杂的Web应用程序,并提供了一套可靠的错误处理机制。 5. 容易上手:Playwright的API设计简洁明了,易于理解和使用。它提供了详细的文档和示例代码,帮助你快速入门。 6. 社区支持:Playwright拥有一个活跃的开源社区,你可以从社区中获得支持、提问问题,并分享经验和最佳实践。 总之,Playwright是一个功能强大、跨浏览器和跨平台的自动化测试框架,可以帮助你构建可靠、稳定的自动化测试。无论是单元测试、集成测试还是端到端测试,Playwright都是一个优秀的选择。你可以通过官方文档和示例来学习更多关于Playwright的使用方法和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值