1. 環境說明

  1. 操作系統:macOS 11.7
  2. Python:3.10.6

2. 安裝

2.1. 創建測試環境

mkdir playwright-demo

cd playwright-demo/

python3 -m venv venv

# 安裝 Pytest 插件

venv/bin/pip3 install pytest-playwright

# 安裝需要的瀏覽器

venv/bin/playwright install

2.2. 添加樣例測試

在當前工作目錄或子目錄內部,創建test_my_application.py文件,其內容如下:

import re

from playwright.sync_api import Page, expect

def test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page(page: Page):

page.goto(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

# Expect a title “to contain” a substring.

expect(page).to_have_title(re.compile(“Playwright”))

# create a locator

get_started = page.locator(“text=Get started”)

# Expect an attribute “to be strictly equal” to the value.

expect(get_started).to_have_attribute(“href”, “/docs/intro”)

# Click the get started link.

get_started.click()

# Expects the URL to contain intro.

expect(page).to_have_url(re.compile(“.*intro”))

2.3. 運行樣例測試

默認情況下,測試運行在 chromium 上,可通過 CLI 選項進行配置,測試以 Headless 模式運行。測試結果和測試日志被展示在終端中。

venv/bin/pytest


3. 編寫測試

Playwright 斷言(assertion)是專門為動態網頁創建的。檢查會自動重試,直到滿足必要的條件。Playwright 自帶 auto-wait,這意味著它在執行操作之前等待元素變為可操作的(actionable)。Playwright 提供 expect 函數來寫斷言。

下面的樣例測試展示了如何寫使用斷言、定位器(locator)和選擇器(selector)的測試。

import re

from playwright.sync_api import Page, expect

def test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page(page: Page):

page.goto(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

# Expect a title “to contain” a substring.

expect(page).to_have_title(re.compile(“Playwright”))

# create a locator

get_started = page.locator(“text=Get started”)

# Expect an attribute “to be strictly equal” to the value.

expect(get_started).to_have_attribute(“href”, “/docs/intro”)

# Click the get started link.

get_started.click()

# Expects the URL to contain intro.

expect(page).to_have_url(re.compile(“.*intro”))

3.1. 斷言

Playwright 提供 expect 函數,它會一直等待,直到滿足預期條件。

import re

from playwright.sync_api import expect

expect(page).to_have_title(re.compile(“Playwright”))

3.2. 定位器

定位器(Locators)是 Playwright 的自動等待和重試能力的核心部分。定位器表示一種隨時在網頁上查找元素的方法,用于在元素上執行諸如 .click、.fill 之類的操作。可以使用 page.locator(selector, **kwargs) 方法創建自定義的定位器。

from playwright.sync_api import expect

get_started = page.locator(“text=Get started”)

expect(get_started).to_have_attribute(“href”, “/docs/installation”)

get_started.click()

選擇器(Selectors)是用于創建定位器的字符串。Playwright 支持許多不同的選擇器,比如 Text、CSS、XPath 等。通過 in-depth guide 文檔,了解更多關于可用的選擇器以及如何進行選擇的信息。

from playwright.sync_api import expect

expect(page.locator(“text=Installation”)).to_be_visible()

3.3. 測試隔離

Playwright Pytest 插件基于 test fixture(比如 built in page fixture)的概念,它會被傳給你的測試。由于瀏覽器上下文,在測試之間,頁面(page)彼此隔離,這相當于開啟新的瀏覽器行為,每個測試獲得新環境,即使在一個瀏覽器中運行多個測試時,也是如此。

from playwright.sync_api import Page

def test_basic_test(page: Page):

# …

3.4. 使用測試鉤子

你可以使用各種各樣的 fixtures 來在你的測試之前或之后執行代碼,以及在它們之間共享對象。函數(function)作用域的 fixture 具有 beforeEach/afterEach 一樣的自動使用行為。模塊(module)作用域的 fixture 具有 beforeAll/afterAll 一樣的自動使用行為,它會在所有測試之前和所有測試之后運行。

import pytest

from playwright.sync_api import Page, expect

@pytest.fixture(scope=”function”, autouse=True)

def before_each_after_each(page: Page):

print(“beforeEach”)

# Go to the starting url before each test.

page.goto(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

yield

print(“afterEach”)

def test_main_navigation(page: Page):

# Assertions use the expect API.

expect(page).to_have_url(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

4. 運行測試

你可以運行單個測試、一組測試或全部測試。測試可以運行在一種或多種瀏覽器上。默認情況下,測試以 headless 方式運行,這意味著在運行測試時,不會打開瀏覽器窗口,可以在終端中看到結果。通過使用 –headed 標記,可以以 headed 模式運行測試。

– 在 Chromium 上運行測試

pytest

– 運行單個測試文件

pytest test_login.py

– 運行一組測試文件

pytest tests/todo-page/ tests/landing-page/

– 使用函數名運行測試

pytest -k “test_add_a_todo_item”

– 以有頭(headed)模式運行測試

pytest –headed test_login.py

– 在指定的瀏覽器上運行測試

pytest test_login.py –browser webkit

– 在多種瀏覽器上運行測試

pytest test_login.py –browser webkit –browser firefox

– 并行運行測試

pytest –numprocesses auto

(假定已安裝 pytest-xdist,查看 here 獲取更多信息。)

4.1. 運行測試

因為 Playwright 運行在 Python 中,所以可以使用 debugger 調試它。Playwright 自帶 Playwright Inspector,它允許你逐步通過 Playwright API 調用,查看它們的調試日志,以及探索選擇器(selectors)。

PWDEBUG=1 pytest -s


查看我們的調試指南(debugging guide)來了解關于 Playwright Inspector 以及使用瀏覽器開發者工具(Browser Developer tools)進行調試的更多信息。

5. 測試生成器

Playwright 具有開箱即用的生成測試的能力,這是快速開始測試的好方法。它會打開兩個窗口,一個是瀏覽器窗口,通過它你可以與希望測試的網站進行交互,另一個是 Playwright Inspector 窗口,通過它你可以錄制測試、拷貝測試、清除測試以及改變測試的語言。

你將學習:

– How to generate tests with Codegen

5.1. 運行代碼生成器(Codegen)

playwright codegen Fast and reliable end-to-end testing for modern web apps | Playwright

運行 codegen,然后在瀏覽器中執行操作。Playwright 會為用戶的交互生成代碼。Codegen 會嘗試生成彈性的基于文本的選擇器。

當你完成與頁面的交互時,按下record按鈕停止錄制,使用copy按鈕把生成的代碼拷貝到編輯器。

使用 clear 按鈕清除代碼,重新開始錄制。完成時,關閉 Playwright Inspector 窗口,或停止終端命令。

要了解有關生成測試的更多信息,請查看 Codegen 的詳細指南。

6. 追蹤查看器(Trace Viewer)

Playwright 追蹤查看器是一個 GUI 工具,它使你可以探查你的測試中記錄的 Playwright 追蹤,你可以在測試的每個操作中來回移動,可視化地查看每個操作期間正在發生什么。

你將學習:

– 如何記錄追蹤

– 如何打開 HTML 報告

– 如何打開追蹤查看器

6.1. 記錄追蹤

像下面一樣使用 browser_context.tracing API 記錄追蹤:

browser = chromium.launch()

context = browser.new_context()

# Start tracing before creating / navigating a page.

context.tracing.start(screenshots=True, snapshots=True, sources=True)

page.goto(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

# Stop tracing and export it into a zip archive.

context.tracing.stop(path = “trace.zip”)

這將記錄追蹤,并把它放到名稱為trace.zip的文件中。

6.2. 打開追蹤

你可以使用 Playwright CLI 打開保存的追蹤。

playwright show-trace trace.zip

6.3. 查看追蹤

通過單擊每個操作或使用時間軸懸停來查看測試的追蹤,以及查看操作前后的頁面狀態。在測試的每個步驟期間查看日志、源和網絡。追蹤查看器創建 DOM 快照,因此你可以與它進行交互,打開開發者工具(devtools)等。

要了解更多信息,請查看 Trace Viewer 的詳細指南。

7. Pytest 插件參考

Playwright 提供 Pytest 插件來編寫端到端的測試。如果想要使用它,請參考 getting started guide。

7.1. 用法

使用 Pytest CLI 運行測試:

pytest –browser webkit –headed

如果你想自動地添加 CLI 參數,請使用 pytest.ini 文件。

7.2. CLI 參數

  1. – –headed:以有頭模式運行測試(默認:無頭)
  2. – –browser:用不同的瀏覽器 chromium、firefox、webkit 運行測試。可以指定多次(默認:所有瀏覽器)
  3. – –browser-channel:使用的 Browser channel
  4. – –slow-mo:使用慢動作運行測試
  5. – –device:模擬的設備(Device)
  6. – –output:用于測試生成的制品(aritifact)的目錄(默認:test-results)
  7. – –tracing:是否為每次測試記錄追蹤(trace)。on、off 或 retain-on-failure(默認:off)
  8. – –video:是否為每次測試錄制視頻。on、off 或 retain-on-failure(默認:off)
  9. – –screenshot:是否在每次測試后,自動地捕獲截屏。on, off, or only-on-failure (默認:off)

7.3. Fixture

該插件給 pytest 配置 Playwright 特定的 fixture(fixtures for pytest)。為使用這些 fixture,使用 fixture 名稱作為測試函數的參數。

def test_my_app_is_working(fixture_name):

# Test using fixture_name

# …

函數作用域:這些 fixture 在測試函數請求時創建,在測試結束時銷毀。

  1. – context:用于測試的新瀏覽器上下文(browser context)
  2. – page:用于測試的新瀏覽器頁面(browser page)

會話作用域:這些 fixture 在測試函數請求時創建,在測試結束時銷毀。

  1. – playwright:Playwright 實例
  2. – browser_type:當前瀏覽器的 BrowserType 實例
  3. – browser:Playwright 啟動的 Browser 實例
  4. – browser_name:瀏覽器名稱
  5. – browser_channel:瀏覽器通道(channel)
  6. – is_chromium、is_webkit、is_firefox:各自瀏覽器類型的布爾值

自定義 fixture 選項:對于 browser 和 context fixture,使用下面的 fixture 定義自定義啟動選項。

  1. – browser_type_launch_args:重寫用于 browser_type.launch(**kwargs) 的啟動參數。它應該返回字典
  2. – browser_context_args:重寫用于 browser.new_context(**kwargs) 的選項。它應該返回字典

7.4. 并行:同時運行多個測試

如果測試運行在有許多 CPU 的機器上,可以通過使用 pytest-xdist 同時運行多個測試,加快測試套件的整體執行時間。

# install dependency

pip install pytest-xdist

# use the –numprocesses flag

pytest –numprocesses auto

根據硬件和測試的特性,可以將 numprocesses 設置為 2 到機器上 CPU 數量之間的任意值。如果設置得過高,可能產生非預期行為。

有關 pytest 選項的常用信息,請參考 Running Tests。

7.5. 示例

配置 Mypy 類型以自動補全

# test_my_application.py

from playwright.sync_api import Page

def test_visit_admin_dashboard(page: Page):

page.goto(“/admin”)

# …

配置慢動作

使用 –slowmo 參數以慢動作運行測試。

pytest –slowmo 100

通過瀏覽器跳過測試

# test_my_application.py

import pytest

@pytest.mark.skip_browser(“firefox”)

def test_visit_example(page):

page.goto(“Example Domain”)

# …

在特定的瀏覽器上運行測試

# conftest.py

import pytest

@pytest.mark.only_browser(“chromium”)

def test_visit_example(page):

page.goto(“Example Domain”)

# …

使用自定義的瀏覽器通道運行

pytest –browser-channel chrome

# test_my_application.py

def test_example(page):

page.goto(“Example Domain”)

配置 base-url

使用 base-url 參數啟動 Pytest。pytest-base-url 插件允許你通過配置、CLI 參數或像 fixture 一樣設置 base url。

pytest –base-url http://localhost:8080

# test_my_application.py

def test_visit_example(page):

page.goto(“/admin”)

# -> Will result in http://localhost:8080/admin

忽略 HTTPS 錯誤

# conftest.py

import pytest

@pytest.fixture(scope=”session”)

def browser_context_args(browser_context_args):

return {

**browser_context_args,

“ignore_https_errors”: True

}

使用自定義窗口大小

# conftest.py

import pytest

@pytest.fixture(scope=”session”)

def browser_context_args(browser_context_args):

return {

**browser_context_args,

“viewport”: {

“width”: 1920,

“height”: 1080,

}

}

設備仿真

# conftest.py

import pytest

@pytest.fixture(scope=”session”)

def browser_context_args(browser_context_args, playwright):

iphone_11 = playwright.devices[‘iPhone 11 Pro’]

return {

**browser_context_args,

**iphone_11,

}

或通過命令行

–device=”iPhone 11 Pro”。

持久化上下文

# conftest.py

import pytest

from playwright.sync_api import BrowserType

from typing import Dict

@pytest.fixture(scope=”session”)

def context(

browser_type: BrowserType,

browser_type_launch_args: Dict,

browser_context_args: Dict

):

context = browser_type.launch_persistent_context(“./foobar”, **{

**browser_type_launch_args,

**browser_context_args,

“locale”: “de-DE”,

})

yield context

context.close()

從持久化上下文創建測試內部的所有頁面。

與 unittest.TestCase 一起使用

參考下面的示例,了解如何與 unittest.TestCase 一起使用。這有一個限制,即只能指定一個瀏覽器,并且在指定多個瀏覽器時不會生成多個瀏覽器的矩陣。

import pytest

import unittest

from playwright.sync_api import Page

class MyTest(unittest.TestCase):

@pytest.fixture(autouse=True)

def setup(self, page: Page):

self.page = page

def test_foobar(self):

self.page.goto(“https://microsoft.com”)

self.page.locator(“#foobar”).click()

assert self.page.evaluate(“1 + 1”) == 2

7.6. 調試

在代碼中使用 breakpoint() 語句停止執行,獲取 pdb REPL。

def test_bing_is_working(page):

page.goto(“https://bing.com”)

breakpoint()

# …

7.7. 部署到 CI

請查看 guides for CI providers 獲取關于將測試部署到 CI/CD 的信息。

8. 認證

Playwright 可用于需要認證的自動化場景。

用 Playwright 編寫的測試在被稱為瀏覽器上下文(browser contexts)的獨立的干凈的環境中執行。這種隔離模型可以提升復現性,防止級聯測試失敗。新瀏覽器上下文可以加載現有的認證狀態。這可以消除在每個上下文中登錄的需求,加快測試執行的速度。

注意:本指南覆蓋 cookie/token-based 認證(通過 app UI 登陸)。對于 HTTP 認證(HTTP authentication),請使用 browser.new_context(**kwargs)。

8.1. 自動化登錄

Playwright API 可以與登陸表單自動化交互(automate interaction)。

下面的例子自動化登陸到 Github。執行這些步驟之后,瀏覽器上下文將被認證。

page = context.new_page()

page.goto(‘https://github.com/login’)

# Interact with login form

page.get_by_text(“Login”).click()

page.get_by_label(“User Name”).fill(USERNAME)

page.get_by_label(“Password”).fill(PASSWORD)

page.get_by_text(‘Submit’).click()

# Continue with the test

為每次測試重做登錄會減慢測試的執行速度。為緩解這種情況,應該重用現有的認證狀態。

8.2. 重用簽入狀態

Playwright 提供在測試中重用簽入(signed-in)狀態的方式。通過該方式,可以只登陸一次,然后跳過所有測試的登陸步驟。

Web 應用使用基于 Cookie 或基于 Token 的認證,認證狀態被當作 cookies 存儲,或存儲在 local storage 中。Playwright 提供 browserContext.storageState(options) 方法,可使用它從已認證上下文中獲取存儲狀態,然后使用預填充狀態創建新上下文。

Cookie 和 Local Storage 狀態可以跨不同的瀏覽器使用。它們依賴應用程序的認證模型:有些應用程序可能同時需要 Cookie 和 Local Storage。

下面的代碼片段從已認證上下文中獲取狀態,然后使用該狀態創建新上下文。

# Save storage state into the file.

storage = context.storage_state(path=”state.json”)

# Create a new context with the saved storage state.

context = browser.new_context(storage_state=”state.json”)

8.3. Session Storage

session storage 很少用于存儲與登陸狀態相關的信息。Session Storage 特定于特定的域,頁面加載時它不會持久化。Playwright 沒提供用于持久化 Session Storage 的 API,但下面的片段可用于保存/加載 Session Storage。

import os

# Get session storage and store as env variable

session_storage = page.evaluate(“() => JSON.stringify(sessionStorage)”)

os.environ[“SESSION_STORAGE”] = session_storage

# Set session storage in a new context

session_storage = os.environ[“SESSION_STORAGE”]

context.add_init_script(“””(storage => {

if (window.location.hostname === ‘Example Domain’) {

const entries = JSON.parse(storage)

for (const [key, value] of Object.entries(entries)) {

window.sessionStorage.setItem(key, key)

}

}

})(‘””” + session_storage + “‘)”)

8.4. 多因子認證

使用多因子認證(MFA)的賬戶無法完全自動化,需要人工干預。持久化認證可用于部分自動化 MFA 場景。

8.4.1 持久化認證

注意持久化認證不適用于 CI 環境,因為它依賴磁盤位置。用戶數據目錄特定于瀏覽器類型,不能跨瀏覽器類型共享。

用戶數據目錄可以與 browser_type.launch_persistent_context(user_data_dir, **kwargs) API 一起使用。

from playwright.sync_api import sync_playwright

with sync_playwright() as p:

user_data_dir = ‘/path/to/directory’

browser = p.chromium.launch_persistent_context(user_data_dir, headless=False)

# Execute login steps manually in the browser window

生命周期

1. 在磁盤上創建用戶數據目錄

2. 使用用戶數據目錄啟動持久化上下文,然后登陸 MFA 賬戶

3. 重用用戶數據目錄來運行自動化場景

9. 事件

Playwright 允許監聽發生在 Web 頁面上的多種類型的事件,比如網絡請求、子頁面的創建、專用 Worker 等。可以通過多種方式訂閱這些事件,比如等待事件或添加/移除事件監聽者。

9.1. 等待事件

大多數時間,腳本需要等待特定的事件發生。下面是一些典型的事件等待模式。

使用 page.expect_request(url_or_predicate, **kwargs) 等待擁有指定 URL 的請求:

with page.expect_request(“**/*logo*.png”) as first:

page.goto(“https://wikipedia.org”)

print(first.value.url)

等待彈出窗口:

with page.expect_popup() as popup:

page.evaluate(“window.open()”)

popup.value.goto(“https://wikipedia.org”)

9.2. 添加/移除事件監聽者

有時,事件發生在隨機的事件,而不是等待它們,需要處理它們。Playwright 支持訂閱/取消訂閱事件的傳統語言機制:

def print_request_sent(request):

print(“Request sent: ” + request.url)

def print_request_finished(request):

print(“Request finished: ” + request.url)

page.on(“request”, print_request_sent)

page.on(“requestfinished”, print_request_finished)

page.goto(“https://wikipedia.org”)

page.remove_listener(“requestfinished”, print_request_finished)

page.goto(“https://www.openstreetmap.org/”)

9.3. 添加一次性監聽者

如果特定事件需要被處理一次,那么可以使用便捷的 API:

page.once(“dialog”, lambda dialog: dialog.accept(“2021”))