前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快速进行UI自动化

快速进行UI自动化

作者头像
赵云龙龙
发布2024-01-15 12:04:37
1830
发布2024-01-15 12:04:37
举报
文章被收录于专栏:python爱好部落python爱好部落

最近团队少了好些人,有点忙不过来,虽然是有覆盖好多API测试,但是端到端的测试,还是少不了UI。

发现微软有个好工具,Playwright, 关键是可以录制,比起传统的selenium, 速度快很多,不需要那么繁杂的定位元素,等待等各种操作。关键是,不需要那么多版本的driver.

如果实在忙不过来的时候,还可以录制一下,作为回归的测试。

跨浏览器和平台

跨浏览器。Playwright 支持所有现代渲染引擎,包括 Chromium、WebKit 和 Firefox。

跨平台。在 Windows、Linux 和 macOS 上进行本地测试或在 CI 上进行无头或有头测试。

跨语言。在TypeScript、JavaScript、Python、.NET、Java中使用 Playwright API 。

测试移动网络。适用于 Android 和 Mobile Safari 的 Google Chrome 浏览器的本机移动仿真。相同的渲染引擎适用于您的桌面和云端。

稳定性

自动等待。Playwright 在执行动作之前等待元素可操作。它还具有一组丰富的内省事件。两者的结合消除了人为超时的需要——这是不稳定测试的主要原因。

Web优先断言。Playwright 断言是专门为动态网络创建的。检查会自动重试,直到满足必要的条件。

追踪。配置测试重试策略,捕获执行跟踪、视频、屏幕截图。

运行机制

浏览器在不同进程中运行属于不同来源的 Web 内容。Playwright 与现代浏览器架构保持一致,并在进程外运行测试。这使得 Playwright 摆脱了典型的进程内测试运行器的限制。

测试跨越多个选项卡、多个来源和多个用户的场景。为不同的用户创建具有不同上下文的场景,并在您的服务器上运行它们,所有这些都在一次测试中完成。

可信事件。悬停元素,与动态控件交互,产生可信事件。Playwright 使用与真实用户无法区分的真实浏览器输入管道。

测试框架,穿透 Shadow DOM。Playwright 选择器穿透影子 DOM 并允许无缝地输入帧。

完全隔离-快速执行

浏览器上下文。Playwright 为每个测试创建一个浏览器上下文。浏览器上下文相当于一个全新的浏览器配置文件。这提供了零开销的完全测试隔离。创建一个新的浏览器上下文只需要几毫秒。

登录一次。保存上下文的身份验证状态并在所有测试中重用它。这绕过了每个测试中的重复登录操作,但提供了独立测试的完全隔离。

强大的工具

代码生成器。通过记录您的操作来生成测试。将它们保存为任何语言。

调试。检查页面、生成选择器、逐步执行测试、查看点击点、探索执行日志。

跟踪查看器。捕获所有信息以调查测试失败。Playwright 跟踪包含测试执行截屏、实时 DOM 快照、动作资源管理器、测试源等等。

安装 playwright:

代码语言:javascript
复制
pip install playwright

安装所需的浏览器 chromium,firefox 和 webkit:

代码语言:javascript
复制
playwright install

仅需这一步即可安装所需的浏览器,并且不需要安装驱动包了(解决了selenium启动浏览器,总是要找对应驱动包的痛点)

三,简单使用

Playwright 支持2种运行方式:同步和异步。以下为同步:

代码语言:javascript
复制
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)          # 启动 chromium 浏览器
    page = browser.new_page()              # 打开一个标签页
    page.goto("https://www.baidu.com")     # 打开百度地址
    print(page.title())                    # 打印当前页面title
    browser.close()                        # 关闭浏览器对象

如果不习惯with语句,也可以用start() 和stop() 的方式:

代码语言:javascript
复制
from playwright.sync_api import sync_playwright
playwright = sync_playwright().start()
browser = playwright.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.baidu.com/")
browser.close()
playwright.stop()

四:定位元素

playwright 可以通过 CSS selector, XPath selector, HTML 属性(比如 id, data-test-id)或者是 text 文本内容定位元素。

Selector 选择器

操作元素,可以先定位再操作

代码语言:javascript
复制
   # 先定位再操作
    page.locator('#kw').fill("playwright")
    page.locator('#su').click()

也可以直接调用fill 和 click 方法,传Selector选择器

代码语言:javascript
复制
page.fill('#kw', "playwright")
page.click('#su')

CSS 或 XPath 选择器

可以使用xpath 和 css 元素

代码语言:javascript
复制
# CSS and XPath
page.fill('css=#kw', "playwright")
page.click('xpath=//*[@id="su"]')

当 DOM 结构发生变化时,这些选择器可能会中断。长 CSS 或 XPath 链是导致测试不稳定。

text 文本选择器

文本选择器是一个非常实用的定位方式,根据页面上看到的text文本就可以定位了,比如我们经常在selenium中使用xpath 的文本选择器定位

完全匹配文本 //*[text()="百度一下"]

包含某个文本 //*[contains(text(),"百度一下")]

playwright 封装了text文本定位的方式,也可以支持2种文本定位方式

代码语言:javascript
复制
page.click("text=百度一下")  # 模糊匹配
page.click("text='百度一下 '")  # 完全匹配

关键这玩意,还可以拦截API请求,这个在测UI的过程中,就可以获取API的数据信息,就可以作为爬虫使用了。

截图

截取当前屏幕

如果仅仅截取当前屏幕(浏览器)上能看到的部分,那么可以使用如下语法:

代码语言:javascript
复制
page.screenshot(path="截图保存路径")

截取整个页面

有时候,页面可能会比较长,一个屏幕无法全部展示出来。如果想截取整个页面,怎么办呢?

page.screenshot提供了full_page参数,语法如下:

代码语言:javascript
复制
page.screenshot(path="截图保存路径",full_page=True)

截取页面的一部分

有时候,我们可能只想截取页面的一部分,那么,Playwright也支持将想要截取的部分筛选出来,然后调用截图API进行截图。

示例:打开主页,截取页面部分,保存为一张图片。

代码语言:javascript
复制
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
      browser = p.chromium.launch(headless=False)
      page = browser.new_page()
      page.goto('https:')
      page.locator('.main-footer').screenshot(path=f'screenshot3.png')
      
      page.wait_for_timeout(2000)
      browser.close()

简单录制了一个脚本:

代码语言:javascript
复制
from playwright.sync_api import Playwright, sync_playwright, expect
import re
import json

def handle(response):
    if response is not None:
        if not response.url.endswith(("png","jpg","css","js","ico","woff","woff2","ttf","svg","eot")):
            print(response.request.url)
            if response.request.method == "POST":
                print(response.request.post_data)
            else:
                print(response.request.method)
                print(response.request.headers)
                if response.request.headers.get("Content-Type") == "application/json":
                    print(response.request.post_data_json)
                if "authorization" in response.request.headers:
                    print(response.request.headers.get("authorization"))

                if isinstance(response.json(),dict):
                    print(response.json())
                if isinstance(response.text(),list):
                    print(response.text())
                if isinstance(response.text(),str):
                    print(response.text())
                if isinstance(response.text(),int):
                    print(response.text())
                if isinstance(response.text(),float):
                    print(response.text())
                if isinstance(response.text(),bool):
                    print(response.text())

def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    # page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
    # page.route(re.compile(r"(\.png$)|(\.jpg$)"), lambda route: route.abort())
    #   # 监听请求事件
    # page.on("request", lambda request: print("request: ", request.method, request.url))
    # # 监听响应事件
    # page.on("response", lambda response: print("response: ", response.status, response.url,"\n",response.body,"\n",response. headers))
    page.on("response", lambda response:handle(response=response))
    page.goto("https://10.4.131.110/console/")
    page.frame_locator("iframe").get_by_placeholder("请输入账号").click()
    page.frame_locator("iframe").get_by_placeholder("请输入账号").fill("admin")
    page.frame_locator("iframe").get_by_placeholder("请输入账号").press("Enter")
    page.frame_locator("iframe").get_by_placeholder("请输入密码").click()
    page.frame_locator("iframe").get_by_placeholder("请输入密码").fill("eisoo.com123")
    page.frame_locator("iframe").get_by_placeholder("请输入密码").press("Enter")
    page.get_by_role("menuitem", name=" 安全管理").click()

    # 验证:
    # blog:https://www.cnblogs.com/yoyoketang/

    #from playwright.sync_api import expect

    # locator = page.get_by_label("Subscribe to newsletter")
    # expect(locator).to_be_checked()

    # locator = page.locator('.my-element')
    #expect(locator).to_be_visible()

    # 截图 单个元素
    # page.locator(".header").screenshot(path="screenshot.png")

    # 截取长图
    #page.screenshot(path="screenshot.png", full_page=True)

    # 截取整个页面
    # page.screenshot(path="screenshot.png")

    page.get_by_text("文档安全策略配置").click()
    page.get_by_text("权限申请策略").click()
    page.get_by_role("button", name=" 新建策略").click()
    page.get_by_label("部分文档库").check()
    page.get_by_role("button", name="选择").click()
    page.locator("div").filter(has_text=re.compile(r"^1$")).nth(1).click()
    page.get_by_role("button", name="确定").click()
    page.get_by_label("部分用户").check()
    page.locator("form").filter(has_text="适用范围:该条策略将应用到所选用户上全部用户部分用户选择").get_by_role("button", name="选择").click()
    page.get_by_role("link", name=" a").click()
    page.get_by_role("button", name="").click()
    page.locator("form").filter(has_text="适用范围:该条策略将应用到所选用户上全部用户部分用户选择").get_by_role("button", name="选择").click()
    page.get_by_role("link", name=" 组织结构").click()
    page.locator("div").filter(has_text=re.compile(r"^组织结构$")).nth(1).click()
    page.locator("body").press("Enter")
    page.get_by_role("button", name="").click()
    page.locator("form").filter(has_text="适用范围:该条策略将应用到所选用户上全部用户部分用户选择").get_by_role("button", name="选择").click()
    page.get_by_text("a").click()
    page.get_by_role("button", name="确定").click()
    page.locator("div").filter(has_text=re.compile(r"^管控文档库文档库:该条策略将应用到所选文档库上全部文档库部分文档库1选择适用范围适用范围:该条策略将应用到所选用户上全部用户部分用户a选择$")).first.click()
    page.get_by_role("button", name=" 执行规则").click()
    page.get_by_role("button", name="close").click()
    page.get_by_role("button", name="确定").click()
    page.locator("div").filter(has_text=re.compile(r"^审核$")).nth(1).click()
    page.get_by_role("button", name="设置流程").click()
    page.get_by_role("dialog").filter(has_text="审核环节点击审核环节,可设置此环节的审核员及审核模式。1 / 3上一步下一步").get_by_role("button", name="下一步").click()
    page.get_by_role("dialog").filter(has_text="添加环节点击添加审核环节,可设置不同审核模式下的多层级审批。2 / 3上一步下一步").get_by_role("button", name="下一步").click()
    page.get_by_role("button", name="我知道了").click()
    page.locator("div").filter(has_text=re.compile(r"^设置审核模式和审核员$")).nth(2).click()
    page.get_by_role("radio", name="指定用户审核").click()
    page.get_by_role("treeitem", name="  a").locator("i").click()
    page.get_by_text("cc", exact=True).click()
    page.get_by_role("button", name="close drawer").click()
    page.get_by_text("从已有审核流程模板中选择>> 发起审核您还未对此环节进行设置。审核设置审核模式和审核员您还未对此环节进行设置。流程结束").click()
    page.get_by_role("button", name="close drawer").click()
    page.close()

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

看起来虽然好像很乱,但是运行起来还不错。

发现很多前端都是框架写的,定位元素不容易,就不需要去做验证了,用接口来验证也就可以了,这样也就达到了验证效果。

如果接口录制比较顺利,还可以做流量回放来测试。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python粉丝团 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档