微软出品的UI自动化测试工具Playwright(三)
微软出品的UI自动化测试工具Playwright(三)
| 网址 | 说明 | 
|---|---|
| https://playwright.dev/ | 官网首页 | 
| https://playwright.dev/python/docs/intro | Python部分入口 | 
| https://github.com/microsoft/playwright-python | github,python入口 | 
| https://github.com/microsoft/playwright-python/releases | python部分的release notes | 
本文基于 playwright 1.32.1 发布于 2023-3-30
学习前你得有html、css、xpath等基础,最好有selenium基础,我是对比来讲解的
微软出品的UI自动化测试工具Playwright(一)
微软出品的UI自动化测试工具Playwright(二)
微软出品的UI自动化测试工具Playwright(三)
⑤实例
通过一个个实例来讲述Playwright的能力
实例一: 打开某某网站
- 
参考代码
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://www.baidu.com") - 
执行效果:打开百度后自动关闭
 - 
对比selenium,这是有点麻烦的
 
- 
playwright是个包(文件夹),里面主要是3个部分
- sync_api:同步,入门你用的最多的是这个
 - driver
 - async_api:异步
 
 - 
sync_playwright是一个函数,返回值类型是PlaywrightContextManager
def sync_playwright() -> PlaywrightContextManager: return PlaywrightContextManager() - 
从名字上看得出是个上下文管理器,所以你可以用with来处理
钻进去你能看到__enter__和__exit def __enter__(self) -> SyncPlaywright: - 
而这个SyncPlaywright是有很多属性的,主要的就是我们要打开的几个浏览器
def chromium(self) -> "BrowserType": """Playwright.chromium This object can be used to launch or connect to Chromium, returning instances of `Browser`. Returns ------- BrowserType """ return mapping.from_impl(self._impl_obj.chromium) def firefox(self) -> "BrowserType": def webkit(self) -> "BrowserType": - 
BrowserType则是浏览器类型类,它的launch方法是重要的方法,参数非常多,一般用到的就是headless,赋值False就是有头(能看到页面)。返回的是浏览器实例。
 - 
在playwright中你仅有一个浏览器实例是作不了什么的,你必须要打开一个页面
 - 
Browser有一些方法属性,其中new_page是用来打开新的页面的,返回类型是Page
 - 
玩过selenium的同学应该知道,Page对象是你最多了解的部分
 - 
Page对象属性方法详见附录一
 
实例二: 登录开源论坛
- 
前面了解了打开网页
 - 
下面就涉及UI自动化测试最重要的定位了
 - 
参考代码
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser_type = p.chromium browser = browser_type.launch(headless=False) page = browser.new_page() page.goto("http://114.116.2.138:8090/forum.php") page.locator('#ls_username').fill('admin') page.locator('#ls_password').fill('123456') page.locator('.pn.vm').click() page.wait_for_timeout(5000) - 
可以看到我们使用了css语法来定位
 - 
page对象的locator方法中就可以去塞一个css表达式来定位元素
 - 
而这个元素也是非常重要的对象,其类型是Locator,这个对象的属性方法可以参考附录二
 - 
此处的等待要注意,在页面上等待不建议使用time.sleep,而是应该用page的wait_for_timeout
 - 
参考代码2
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser_type = p.chromium browser = browser_type.launch(headless=False) page = browser.new_page() page.goto("http://114.116.2.138:8090/forum.php") page.fill('#ls_username','admin') page.fill('#ls_password','123456') page.click('.pn.vm') page.wait_for_timeout(5000) - 
的确,locator都可以省略的,直接在fill或者click上写表达式
 
实例三: 获取百度热点
- 
参考代码
from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser = pw.chromium.launch(headless=False) page = browser.new_page() page.goto('https://www.baidu.com') hot_news = page.locator('.title-content-title').all_text_contents() for hot_new in hot_news: print(hot_new) - 
上述代码是ok的,但是你如果从selenium转过来,是比较别扭的
 - 
因为page.locator('.title-content-title')定位元素在实例二中提到了,但此处表达式.title-content-title是有多个元素的
 - 
在selenium中如果定位到了多个,你操作的是第一个,但playwright用locator的做法不一样,这多少有点不习惯。
 - 
此处all_text_contents()的返回是个list,其内容就是我们热点的文本信息
['红树林边的瞩望', '带你体验中老友谊之旅', '中央转移支付首破十万亿 钱去哪儿了', '云南玉溪山火蔓延 火场燃烧迅猛', '银行发现7位数存款5年未动急寻人', '台媒:大陆禁航从27分钟变6小时'] 
- 
我们可以看看如果定位到一个元素的话,它的输出是没问题的
from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser = pw.chromium.launch(headless=False) page = browser.new_page() page.goto('https://www.baidu.com') # .hotsearch-item:nth-child(1) .title-content-title 这个表达式就定位到一个元素 hot_news = page.locator('.hotsearch-item:nth-child(1) .title-content-title') print(hot_news.text_content()) - 
如果你定位到多个元素,就不行了
from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser = pw.chromium.launch(headless=False) page = browser.new_page() page.goto('https://www.baidu.com') hot_news = page.locator('.title-content-title') print(hot_news.text_content())Traceback (most recent call last): File "D:\pythonProject\AutoTest\DemoPlaywright0413\demos\demo_openpage.py", line 7, in <module> print(hot_news.text_content()) File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\sync_api\_generated.py", line 17562, in text_content self._sync(self._impl_obj.text_content(timeout=timeout)) File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_sync_base.py", line 104, in _sync return task.result() File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_locator.py", line 564, in text_content return await self._frame.text_content( File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_frame.py", line 605, in text_content return await self._channel.send("textContent", locals_to_params(locals())) File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_connection.py", line 61, in send return await self._connection.wrap_api_call( File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_connection.py", line 461, in wrap_api_call return await cb() File "D:\pythonProject\AutoTest\DemoPlaywright0413\venv\lib\site-packages\playwright\_impl\_connection.py", line 96, in inner_send result = next(iter(done)).result() playwright._impl._api_types.Error: Error: strict mode violation: locator(".title-content-title") resolved to 6 elements: 1) <span class="title-content-title">红树林边的瞩望</span> aka get_by_role("link", name=" 红树林边的瞩望") 2) <span class="title-content-title">带你体验中老友谊之旅</span> aka get_by_role("link", name="3 带你体验中老友谊之旅") 3) <span class="title-content-title">中央转移支付首破十万亿 钱去哪儿了</span> aka get_by_role("link", name="1 中央转移支付首破十万亿 钱去哪儿了") 4) <span class="title-content-title">云南玉溪山火蔓延 火场燃烧迅猛</span> aka get_by_role("link", name="4 云南玉溪山火蔓延 火场燃烧迅猛") 5) <span class="title-content-title">银行发现7位数存款5年未动急寻人</span> aka get_by_role("link", name="2 银行发现7位数存款5年未动急寻人") 6) <span class="title-content-title">台媒:大陆禁航从27分钟变6小时</span> aka get_by_role("link", name="5 台媒:大陆禁航从27分钟变6小时") =========================== logs =========================== waiting for locator(".title-content-title") ============================================================ 进程已结束,退出代码为 1 
- 然playwright还有一些其他的定位方法
 
实例四: 验证码破解登录
- 
参考代码
from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser = pw.chromium.launch(headless=False) page = browser.new_page() page.goto('http://121.5.150.55:8090/forum.php') page.locator('#ls_username').fill('admin') page.locator('#ls_password').fill('123456') page.locator('.pn.vm').click() code_bytes = page.locator("[id^='vseccode_cSA']>img").screenshot() import ddddocr ocr = ddddocr.DdddOcr() code_text = ocr.classification(code_bytes) page.locator("input[id^='seccodeverify_cSA']").fill(code_text) page.locator("[name='loginsubmit']").click() - 
在这个案例中,我们主要用到了元素的screenshot(),返回的是一个bytes对象
 - 
然后经过ddddocr处理可以得到其文本
 
实例五: 相对定位
- 
仅供参考
 - 
实例代码
from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser = pw.chromium.launch(headless=False) page = browser.new_page() page.goto('http://121.41.14.39:8088/index.html') page.locator('input:above(#password)').fill('sq1') page.locator('#password:near(#username)').fill('123') page.locator('#code:left-of(img)').fill('999999') page.locator('#submitButton:below(#code)').click() page.wait_for_timeout(5000) 
实例六: 论坛的悬停登录
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
    browser = pw.chromium.launch(headless=False)  
    page = browser.new_page()  
    page.goto('http://114.116.2.138:8090/forum.php')
    qmenu_locator = page.locator('#qmenu')
    qmenu_locator.hover()
    page.click('.xi2>strong')
    page.fill('[id^=username_L]','admin')
    page.fill('[id^=password3_L]','123456')
    page.click('[name=loginsubmit].pn.pnc')
    page.wait_for_timeout(5000)
实例七: 论坛的发帖
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
    browser = pw.chromium.launch(headless=False)  
    page = browser.new_page()  
    page.goto('http://114.116.2.138:8090/forum.php')
    page.fill('#ls_username','admin')
    page.fill('#ls_password','123456')
    page.keyboard.press('Enter')
    page.wait_for_timeout(2000)  
    page.click('text=默认版块')
    page.click('css=#newspecial')
    page.fill('#subject','帖子标题')
    page.frame_locator('#e_iframe').locator('#editorheader+body').fill('帖子内容')  #frame切换
    page.click('#postsubmit')
    page.wait_for_timeout(5000)
附录
Page对象属性方法
| 属性 | 说明 | 
|---|---|
| accessibility | 可访问性 | 
| add_init_script | |
| add_script_tag | |
| add_style_tag | |
| bring_to_front | |
| check | |
| click | |
| close | |
| content | |
| context | 上下文 | 
| dblclick | 双击 | 
| dispatch_event | |
| drag_and_drop | |
| emulate_media | |
| eval_on_selector | |
| eval_on_selector_all | |
| evaluate | |
| evaluate_handle | |
| expect_console_message | |
| expect_download | |
| expect_event | |
| expect_file_chooser | |
| expect_navigation | |
| expect_popup | |
| expect_request | |
| expect_request_finished | |
| expect_response | |
| expect_websocket | |
| expect_worker | |
| expose_binding | |
| expose_function | |
| fill | |
| focus | |
| frame | |
| frame_locator | |
| frames | |
| get_attribute | |
| get_by_alt_text | |
| get_by_label | |
| get_by_placeholder | |
| get_by_role | |
| get_by_test_id | |
| get_by_text | |
| get_by_title | |
| go_back | |
| go_forward | |
| goto | 跳转到指定页面 | 
| hover | |
| inner_html | |
| inner_text | |
| input_value | |
| is_checked | |
| is_closed | |
| is_disabled | |
| is_editable | |
| is_enabled | |
| is_hidden | |
| is_visible | |
| keyboard | 键盘操作入口,返回Keyboard | 
| locator | 定位元素 | 
| main_frame | |
| mouse | 鼠标操作入口,返回Mouse | 
| on | |
| once | |
| opener | |
| pause | |
| press | |
| query_selector | |
| query_selector_all | |
| reload | 页面刷新 | 
| remove_listener | |
| request | |
| route | |
| route_from_har | |
| screenshot | 截图 | 
| select_option | |
| set_checked | |
| set_content | |
| set_default_navigation_timeout | |
| set_default_timeout | |
| set_extra_http_headers | |
| set_input_files | |
| set_viewport_size | |
| tap | |
| text_content | |
| title | 标题 | 
| touchscreen | |
| type | |
| uncheck | |
| unroute | |
| url | url | 
| video | |
| viewport_size | |
| wait_for_event | |
| wait_for_function | |
| wait_for_load_state | |
| wait_for_selector | |
| wait_for_timeout | 等待,参数是毫秒,float | 
| wait_for_url | |
| workers | 
Locator对象属性方法
| 属性 | 说明 | 
|---|---|
| all | 所有元素 | 
| all_inner_texts | |
| all_text_contents | 所有文本内容 | 
| blur | |
| bounding_box | |
| check | |
| clear | 清空 | 
| click | 点击 | 
| count | 元素个数 | 
| dblclick | 双击 | 
| dispatch_event | |
| drag_to | 拖拽 | 
| element_handle | |
| element_handles | |
| evaluate | |
| evaluate_all | |
| evaluate_handle | |
| fill | 输入内容 | 
| filter | |
| first | |
| focus | |
| frame_locator | |
| get_attribute | |
| get_by_alt_text | 通过alt属性的文本定位 | 
| get_by_label | |
| get_by_placeholder | |
| get_by_role | |
| get_by_test_id | |
| get_by_text | |
| get_by_title | |
| highlight | |
| hover | |
| inner_html | |
| inner_text | |
| input_value | |
| is_checked | |
| is_disabled | |
| is_editable | |
| is_enabled | |
| is_hidden | |
| is_visible | |
| last | |
| locator | |
| nth | 下标 | 
| on | |
| once | |
| page | |
| press | |
| remove_listener | |
| screenshot | 元素截图 | 
| scroll_into_view_if_needed | |
| select_option | |
| select_text | |
| set_checked | |
| set_input_files | |
| tap | |
| text_content | |
| type | |
| uncheck | |
| wait_for | 
get_by_role所支持的标签
[
            "alert",
            "alertdialog",
            "application",
            "article",
            "banner",
            "blockquote",
            "button",
            "caption",
            "cell",
            "checkbox",
            "code",
            "columnheader",
            "combobox",
            "complementary",
            "contentinfo",
            "definition",
            "deletion",
            "dialog",
            "directory",
            "document",
            "emphasis",
            "feed",
            "figure",
            "form",
            "generic",
            "grid",
            "gridcell",
            "group",
            "heading",
            "img",
            "insertion",
            "link",
            "list",
            "listbox",
            "listitem",
            "log",
            "main",
            "marquee",
            "math",
            "menu",
            "menubar",
            "menuitem",
            "menuitemcheckbox",
            "menuitemradio",
            "meter",
            "navigation",
            "none",
            "note",
            "option",
            "paragraph",
            "presentation",
            "progressbar",
            "radio",
            "radiogroup",
            "region",
            "row",
            "rowgroup",
            "rowheader",
            "scrollbar",
            "search",
            "searchbox",
            "separator",
            "slider",
            "spinbutton",
            "status",
            "strong",
            "subscript",
            "superscript",
            "switch",
            "tab",
            "table",
            "tablist",
            "tabpanel",
            "term",
            "textbox",
            "time",
            "timer",
            "toolbar",
            "tooltip",
            "tree",
            "treegrid",
            "treeitem",
        ]
元素状态
| 元素状态(英) | 元素状态(中) | 说明(英) | 
|---|---|---|
| Attached | 附着 | Element is considered attached when it is connected to a Document or a ShadowRoot. | 
| 元素连接到DOM或者ShadowRoot则认为元素是附着的 | ||
| Visible | 可见 | Element is considered visible when it has non-empty bounding box and does not have visibility:hidden computed style. Note that elements of zero size or with display:none are not considered visible. | 
元素的可见意味着它有边界并且没有visibility:hidden,注意元素大小为0或者具有 display:none则认为是不可见的 | 
||
| Stable | 稳定的 | Element is considered stable when it has maintained the same bounding box for at least two consecutive animation frames | 
| 元素在至少两个连续的动画帧中保持相同的边界框时被认为是稳定的 | ||
| Enabled | 使能 | Element is considered enabled unless it is a <button>, <select>, <input> or <textarea> with a disabled property. | 
元素如果是 <button>, <select>, <input> or <textarea>且具有disabled属性就认为是非使能的,否则都是使能的 | 
||
| Editable | 可编辑 | Element is considered editable when it is enabled and does not have readonly property set. | 
元素是可编辑的,意味着它一定是使能的且没有readonly属性 | 
||
| Receivces Events | 接收事件 | element is considered receiving pointer events when it is the hit target of the pointer event at the action point. For example, when clicking at the point (10;10), Playwright checks whether some other element (usually an overlay) will instead capture the click at (10;10) |