前言

我正在使用 Playwright 对公司的 H5 项目进行 UI 自动化测试,但是这个 H5 是基于微信浏览器上实现,只是单纯的使用 Playwright 进行模拟测试的话会遇到不少卡点,经过一番折腾后终于完成了模拟测试的实现,这边文章主要是分享下我所遇到的问题和处理方式,希望能帮助遇到同样问题的小伙伴。

整个实现过程中主要解决了如下问题:

前两个问题都比较简单,翻阅网上帖子都可以找到类似答案,后两个问题在每个系统上处理的方案可能都不一样,下边分享下处理如上问题的思路与方式

处理方案

1.使用 Playwright 模拟手机端浏览器分辨率

日常我们使用 web 自动化测试工具打开任一个 H5 页面,默认使用的都是 PC 端的分辨率,如下图这个联通的广告页,它会变形,不方便测试和报告截图

这个问题,使用 Playwright 的浏览器初始化参数 devices 就可以解决,它可以模拟的设备分辨率与 Chrome 一致,下边是实现测试的部分代码,模拟的是 iPhone 12 Pro:

iphone = context.play_sync.devices["iPhone 12 Pro"]
context.context = context.browser.new_context(**iphone)

完成后可以达到如下分辨率

2.修改浏览器签名以跳过微信访问认证

正常我们直接访问公众号的内容,会出现如下提示 “请使用微信浏览器打开链接”。

这个提示一般是对于浏览器签名 user_agent 的验证,只需要修改默认的请求头带上微信浏览器的标识即可。在 Playwright 中的处理则是变更模拟设备的 ['user_agent'] 参数,下边是实现测试的部分代码:

iphone['user_agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x6309080f) XWEB/8461 Flue'
iphone = context.play_sync.devices["iPhone 12 Pro"]
context.context = context.browser.new_context(**iphone)

3.Mock 验签接口绕过登录环节

如上两个方案足以访问大多数的公众号平台,但是对于一些需要进行微信授权登录的平台就需要进一步的处理,我这用手头的系统简单举个例子,当前系统的整个鉴权验证过程大致如下:

可以看到,整个鉴权过程中,系统在拿到 openID 或 unionID 等身份标识后,就不在与微信接口服务进行交互了。我们只需要 mock 返回自定义登录状态的接口,冒充开发者服务器返回预先定义好的身份标识就可以实现登录。

下边是实现 mock 的部分代码(每个系统都不一样,思路供大家参考):
我这准备了一个 handle 文件存储预定义好的身份响应数据,这样在前端请求登录的时候,我可以冒充开发者服务器告知前端它已登录成功

{
  "code": "000000",
  "message": "成功",
  "debugTrace": null,
  "data": {
    "access_token": "76_JVpSad55vObpDKF_UE4HlMExZTtwX6A66WxnWKKY0VAQjTefJmOWfxWi0ap9phdb6kLkABHORZ3ikfYD_9AxAJ4K8E0CjlEMQlgCKuq1EBs",
    "openid": "o33NMs-U0tpD2IB8Oxx11sZwTJ2Y",
    "unionid": "oe-p46Fl1Elb3K7ku2-22cbrZXcA",
  }
}

在 Playwright 注册 mock

context.main_page = MainPage(context.page)
#*/**/public/mobile/login是系统前端的登录请求
context.page.route("*/**/public/mobile/login", handle)

4.Mock 微信浏览器内置对象 wx 规避异常

处理完鉴权后,系统前端可能还存在一些微信浏览器的内置对象调用,如果运行过程中恰好调用了这些方法,会因为 chrome 浏览器没有内置这些对象而导致报错,常见的可能 window.wx。
遇见这种情况的话,可以使用 add_init_script,在页面上下文创建时注入自建的对象,可以规避一部分报错。
下边是部分代码

async def mock_wx_sdk():  
    # 返回一个模拟 wx 对象的 JavaScript 字符串  
    return """  
        Object.defineProperty(window, 'wx', {  
            value: {  
                config: function(options) {  
                    // 模拟 wx.config 的逻辑  
                    console.log('Mock wx.config called with options:', options);  
                },  
                ready: function(callback) {  
                    // 假设 ready 是立即执行的  
                    callback();  
                },  
                error: function(callback) {  
                    // 模拟错误处理的逻辑  
                },  
                // ... 其他需要模拟的 wx API  
            },  
            writable: false, // 防止被重写  
            enumerable: true, // 使其可枚举  
            configurable: false // 防止被删除或修改  
        });  
    """  

async def test_webpage_with_mock_wx():  
    async with async_playwright() as playwright:  
        browser = await playwright.chromium.launch()  
        context = await browser.new_context()  
        page = await context.new_page()  

        # 在页面上下文创建时注入模拟的 wx 对象  
        await page.add_init_script(mock_wx_sdk())  

        # 加载网页  
        await page.goto('your-webpage-url')


↙↙↙阅读原文可查看相关链接,并与作者交流