我正在使用 Playwright 对公司的 H5 项目进行 UI 自动化测试,但是这个 H5 是基于微信浏览器上实现,只是单纯的使用 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)
完成后可以达到如下分辨率
正常我们直接访问公众号的内容,会出现如下提示 “请使用微信浏览器打开链接”。
这个提示一般是对于浏览器签名 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)
如上两个方案足以访问大多数的公众号平台,但是对于一些需要进行微信授权登录的平台就需要进一步的处理,我这用手头的系统简单举个例子,当前系统的整个鉴权验证过程大致如下:
可以看到,整个鉴权过程中,系统在拿到 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)
处理完鉴权后,系统前端可能还存在一些微信浏览器的内置对象调用,如果运行过程中恰好调用了这些方法,会因为 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')