实例讲解Playwright(一)

①Playwright介绍

  • 理念:Playwright enables reliable end-to-end testing for modern web apps

  • Playwright是一个web自动化测试框架

    Playwright is a framework for Web Testing and Automation
    

支持所有的浏览器

  • Test on Chromium, Firefox and WebKit. Playwright has full API coverage for all modern browsers, including Google Chrome and Microsoft Edge (with Chromium), Apple Safari (with WebKit) and Mozilla Firefox.
  • Cross-platform WebKit testing. With Playwright, test how your app behaves in Apple Safari with WebKit builds for Windows, Linux and macOS. Test locally and on CI.
  • Test for mobile移动端测试. Use device emulation to test your responsive web apps in mobile web browsers.
  • Headless and headed有头无头模式. Playwright supports headless (without browser UI) and headed (with browser UI) modes for all browsers and all platforms. Headed is great for debugging, and headless is faster and suited for CI/cloud executions.

快速可靠的执行

  • Auto-wait APIs 自动等待. Playwright interactions auto-wait for elements to be ready. This improves reliability and simplifies test authoring.
  • Timeout-free automation. Playwright receives browser signals, like network requests, page navigations and page load events to eliminate the need for sleep timeouts that cause flakiness.
  • Fast isolation with browser contexts. Reuse a single browser instance for multiple isolated execution environments with browser contexts.
  • Resilient element locators. Playwright can rely on user-facing strings, like text content and accessibility attributes to locate elements. These locators are more resilient than selectors tightly-coupled to the DOM structure.

强大的自动化能力

②安装

  • 建议在虚拟环境中操作,创建好虚拟环境

    python -m venv venv_playwright
    
  • 切换到该环境下

    cd venv_playwright\Scripts\
    activate.bat
    
    (venv_playwright)   # 看到这个就表示切换成功了
    
  • 执行如下命令

    pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
    pip install --upgrade pip
    pip install playwright   # 安装第三方库
    playwright install
    

③定位

  • 定位在UI自动化测试的重要性不需要多说

  • 示例html,搜罗了官网的一些example

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>PlaywrightDEMO</title>
    </head>
    <body>
        <h3>Sign up</h3>
        <label>
          <input type="checkbox" /> Subscribe
        </label>
        <br/>
        <button>Submit</button>
        <hr>
        <button>登录</button>
        <span>Welcome, John</span>
        <label>Password <input type="password" /></label>
        <input type="email" placeholder="name@example.com" />
        <img alt="playwright logo" src="playwright-logo.svg" width="100" />
        <span title='Issues count'>25 issues</span>
        <hr>
            <ul>
              <li>apple</li>
              <li>banana</li>
              <li>orange</li>
            </ul>
        <hr>
        <iframe src="http://114.116.2.138:8090/forum.php" id='if' name='nf' width="600" height="600"></iframe>
    </body>
    </html>
    

get_by_系列定位

  • playwright的定位,个人觉得非常杂乱,其能力应是超过selenium的,但不太易学

  • get_by_有好多,仅供参考之用,返回对象也是Locator

  • 示例代码1 get_by_role

    from playwright.sync_api import sync_playwright
    with sync_playwright() as pw:
        browser = pw.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto(r'D:\pythonProject\AutoTest\DemoPlaywright0413\demos\demo.html')
        print(page.get_by_role('button', name='登录').text_content()) # 定位的是<button>登录</button>
    
    # 要注意的是,你定位
    
  • 示例代码2 get_by_label

        page.get_by_label('Password').fill('123456')
    # 定位的是  <label>Password <input type="password" /></label>
    
  • 示例代码3 get_by_placeholder

        page.get_by_placeholder('name@example.com').fill('123456')
    # 定位的是 <input type="email" placeholder="name@example.com" />
    
  • 示例代码4 get_by_text ,注意可以结合re

    	print(page.get_by_text('Welcome, John').text_content())
        import re
        print(page.get_by_text(re.compile('welcome, john',re.IGNORECASE)).text_content())
        # 定位的是 <span>Welcome, John</span>
    
  • 示例代码5 get_by_alt_text

        pic_bytes = page.get_by_alt_text("playwright logo").screenshot()
        with open('log.png','wb') as f:
            f.write(pic_bytes)
        # get_by_alt_text("playwright logo") 定位的是 <img alt="playwright logo" src="playwright-logo.svg" width="100" />
        # 定位到的
    
  • 示例代码6 get_by_title

        print(page.get_by_title("Issues count").text_content())
        # 定位到的是 <span title='Issues count'>25 issues</span>
    

locator定位

  • 你用的更多的应该是locator定位

  • locator可以配合css或者xpath实现定位,你有这些基础就很容易理解

  • 举例

    page.locator("css=button").click()
    page.locator("button").click()
    
    
    page.locator("xpath=//button").click()
    page.locator("//button").click()
    
    page.locator("text=立即注册").click() # 有点像 LINK_TEXT
    

  • 示例html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Text</title>
    </head>
    <body>
    <a href="https://www.qq.com">
        QQ
    </a>
    <span id="sp" >SPAN</span>
    </body>
    </html>
    
  • 示例代码

    from playwright.sync_api import sync_playwright
    with sync_playwright() as pw:
        browser = pw.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto(r'D:\pythonProject\AutoTest\DemoPlaywright0413\demos\demo_text.html')
        print(page.locator("text=QQ").get_attribute('href'))
        print(page.locator("text=SPAN").get_attribute('id'))
    
  • 可以看到playwright的定位不限于 link text,对普通的text也有定位能力

相对定位

  • 跟selenium4一样,playwright也提供了相对定位
  • 由于一般很少用,本文不阐述,但我也写了一个demo给你
  • 具体可以参考实例五: 相对定位

多元素处理

  • 实际操作过程中一个定位表达式定位到多个元素是非常常见的情况,实例三:获取百度热点就是此类情况

  • selenium对于定位到多个元素,如果用的是find_element,操作的是第一个元素

  • 而playwright不可以!它有自己的一套处理方式。

  • 我们来看一个示例,这里有好几个问题。

  • 示例html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>MultipleElement</title>
    </head>
    <body>
        <ul>
              <li class="item">apple</li>
              <li class="item">banana</li>
              <li class="item">orange</li>
        </ul>
    </body>
    </html>
    
  • 示例代码

    from playwright.sync_api import sync_playwright
    with sync_playwright() as pw:
        browser = pw.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto(r'D:\pythonProject\AutoTest\DemoPlaywright0413\demos\MultipleElement.html')
        print(page.get_by_role('li').text_content())
    
  • 提示报错

    playwright._impl._api_types.TimeoutError: Timeout 30000ms exceeded.
    =========================== logs ===========================
    waiting for get_by_role("li")
    ============================================================
    
  • 准确的来说这个提示仅仅告诉你了超时,但原因其实是你写的li,并不是一个合法的标签,你可以参考附录:get_by_role所支持的标签

  • 对于li标签你要写的是listitem,这里我们也能看出来get_by_role这样的方法的缺陷

  • 修改代码如下

    print(page.get_by_role('listitem').text_content())
    
  • 还是错,提示如下

    playwright._impl._api_types.Error: Error: strict mode violation: get_by_role("listitem") resolved to 3 elements:
        1) <li class="item">apple</li> aka get_by_text("apple")
        2) <li class="item">banana</li> aka get_by_text("banana")
        3) <li class="item">orange</li> aka get_by_text("orange")
    
    =========================== logs ===========================
    waiting for get_by_role("listitem")
    ============================================================
    
  • 这次提示倒是比较清晰了,告诉你定位到了多个

  • 如何解决?

方式一: filter by text

print(page.get_by_role('listitem').filter(has_text='apple').text_content())
  • 也可以结合正则处理
import re
print(page.get_by_role('listitem').filter(has_text=re.compile('APPLE',re.IGNORECASE)).text_content())

方式二: count和nth

  • 示例代码

    count = page.locator('.item').count()
    for index in range(count):
        print(page.locator('.item').nth(index).text_content())
    
  • 这里有点要注意的是,哪怕没有符合给定表达式的元素,这个count也不会报错

    count = page.locator('.ob').count() 
    print(count) # 0
    

方式三: all

  • 示例代码

    elements = page.locator('.item').all() # 理解为所有的元素, 这就有点像
    for element in elements: # 在元素中遍历
        print(element.text_content())
    
  • .all()可以理解为定位到的所有的元素,就有点像find_elements的效果了

方式四: all_text_contents

  • 示例代码

    element_texts = page.locator('.item').all_text_contents()
    for element_text in element_texts:
        print(element_text)
    
  • 这个属性更多用于获取元素的文本,如果要依次点击元素,还是要用上面的方式三

04-18 16:02