天天看點

API 和 UI 自動化測試神器 - Playwright

作者:區塊軟體開發

簡介

Playwright 是微軟開源的端到端(end-to-end)測試架構,可用于現代 Web 應用。Playwright 提供如下特性:

1. 任意浏覽器、任意平台、一種 API

  1. 跨浏覽器:Playwright 支援所有現代渲染引擎,包括 Chromium、WebKit 和 Firefox。
  2. 跨平台:在 Windows、Linux 和 macOS 上,進行本地或 CI 測試(無頭或有頭)。
  3. 跨語言:可在 TypeScript、JavaScript、Python、.NET、Java 中使用 Playwright API。
  4. 測試移動 Web:Android Google Chrome 和移動 Safari 的本地移動仿真。桌面和雲上運作的渲染引擎相同。

2. 彈性、沒有古怪的測試

  1. 自動等待:Playwright 在執行操作前,将等待到元素可被操作。它還有一組豐富的檢查事件。兩者結合可消除對人為逾時的需求 — 這是導緻古怪測試的主要原因。
  2. Web 優先斷言:Playwright 斷言是專門為動态 Web 建立的。檢查将自動重試,直到滿足必要的條件。
  3. 追蹤:配置測試重試政策,捕獲執行蹤迹,錄像,截屏,以防止遺忘。

3. 無需折中、無限制:浏覽器在不同程序中運作屬于不同源的 Web 内容。Playwright 與現代浏覽器架構保持一緻,在程序外運作測試。這使得 Playwright 擺脫典型的程序内測試運作器限制。

  1. 複合一切:橫跨多個頁籤、多個源和多個使用者的測試場景。為不同使用者建立具有不同上下文的場景,并且在伺服器上運作它們,這些都在一個測試中進行。
  2. 可信事件:懸停元素,與動态控件互動,生成可信事件。Playwright 使用真正的浏覽器輸入管道,與真正的使用者沒有差別。
  3. 測試 Frame、穿透 Shadow DOM:Playwright 選擇器穿透 Shadow DOM,允許無縫進入 Frame。

4. 完全隔離、快速執行:

  1. 浏覽器上下文:Playwright 為每個測試建立一個浏覽器上下文。浏覽器上下文等同于全新的浏覽器配置檔案。它提供零開銷的完全測試隔離。建立新浏覽器上下文僅需幾毫秒。
  2. 登入一次:儲存上下文的身份認證狀态,并且在所有測試中重用。避免在每個測試中重複登入,還提供獨立測試的完全隔離。

5. 強大的工具:

  1. 代碼生成:通過錄制操作生成測試。将它們儲存成任何語言。
  2. Playwright 檢查器:檢查頁面,生成選擇器,逐漸完成測試執行,檢視單擊點,探索執行日志。
  3. 追蹤檢視器:捕獲所有資訊以調查測試失敗。Playwright 追蹤包含測試執行錄屏、實時 DOM 快照、操作資料總管、測試源等。

本文測試環境

  1. 作業系統:macOS 12.6
  2. Python:3.10.6(下文以 Python 為例,進行講述)
  3. Playwright:1.30.0

安裝

建立測試環境

mkdir playwright-demo

cd playwright-demo/

python3 -m venv venv

# 安裝 Pytest 插件

venv/bin/pip3 install pytest-playwright

# 安裝需要的浏覽器

venv/bin/playwright install

添加樣例測試

在目前工作目錄或子目錄内部,建立 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(“https://playwright.dev/”)

# 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”))           

運作樣例測試

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

venv/bin/pytest           

編寫測試

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(“https://playwright.dev/”)

# 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”))           

斷言

Playwright 提供 expect (https://playwright.dev/python/docs/test-assertions)函數,它将一直等待,直到滿足預期條件。

import re

from playwright.sync_api import expect

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

定位器

定位器(Locator)是 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()           

選擇器(Selector)是用于建立定位器的字元串。Playwright 支援許多不同的選擇器,比如 Text、CSS、XPath 等。閱讀 in-depth guide 文檔,了解更多關于可用的選擇器以及如何進行選擇的資訊。

from playwright.sync_api import expect

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

測試隔離

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

from playwright.sync_api import Page

def test_basic_test(page: Page):

# …           

使用測試鈎子

可以使用各種各樣的 fixture 在測試之前或之後執行代碼,以及在它們之間共享對象。函數(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(“https://playwright.dev/”)

yield

print(“afterEach”)

def test_main_navigation(page: Page):

# Assertions use the expect API.

expect(page).to_have_url(“https://playwright.dev/”)           

運作測試

可以運作單個測試、一組測試或全部測試。測試可以運作在一種或多種浏覽器上。預設情況下,以 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 (https://playwright.dev/python/docs/test-runners#parallelism-running-multiple-tests-at-once)擷取更多資訊。】

運作測試

因為 Playwright 運作在 Python 中,是以可以使用 Debugger 調試它。Playwright 自帶 Playwright Inspector,它允許你逐漸通過 Playwright API 調用,檢視它們的調試日志,以及探索選擇器(selectors)。

PWDEBUG=1 pytest -s

API 和 UI 自動化測試神器 - Playwright

檢視調試指南(debugging guide)了解關于 Playwright Inspector 以及使用浏覽器開發者工具(Browser Developer tools)進行調試的更多資訊。

測試生成器

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

你将學習:

  1. How to generate tests with Codegen

運作 Codegen

playwright codegen playwright.dev

運作 codegen,然後在浏覽器中執行操作。Playwright 将為使用者的互動生成代碼。Codegen 嘗試生成彈性的基于文本的選擇器。

API 和 UI 自動化測試神器 - Playwright

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

API 和 UI 自動化測試神器 - Playwright

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

如欲了解有關生成測試的更多資訊,請檢視 Codegen 的詳細指南。

追蹤檢視器(Trace Viewer)

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

你将學習:

  1. 如何記錄追蹤
  2. 如何打開 HTML 報告
  3. 如何打開追蹤檢視器

記錄追蹤

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

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(“https://playwright.dev”)

# Stop tracing and export it into a zip archive.

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

這将記錄追蹤,把它導出到名稱為 trace.zip 的檔案中。

打開追蹤

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

playwright show-trace trace.zip

檢視追蹤

通過單擊每個操作或使用時間軸懸停,檢視測試的追蹤,以及檢視操作前後的頁面狀态。在測試的每個步驟期間檢視日志、源和網絡。追蹤檢視器建立 DOM 快照,是以你可以與它進行互動,打開開發者工具(devtools)等。

API 和 UI 自動化測試神器 - Playwright

如欲了解更多資訊,請檢視 Trace Viewer 的詳細指南。

Pytest 插件參考

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

用法

使用 Pytest(https://docs.pytest.org/en/stable/) CLI 運作測試:

pytest –browser webkit –headed

如果你想自動地添加 CLI 參數,而不是指定它們,請使用 pytest.ini 檔案。

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:https://playwright.dev/python/docs/trace-viewer)。on、off 或 retain-on-failure(預設:off)
  8. –video:是否為每次測試錄制視訊。on、off 或 retain-on-failure(預設:off)
  9. –screenshot:是否在每次測試後,自動地捕獲截圖。on, off, or only-on-failure (預設:off)

Fixture

該插件為 pytest 配置 Playwright 特定的 fixture。為使用這些 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) 的選項。它應該傳回字典

并行:同時運作多個測試

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

# install dependency

pip install pytest-xdist

# use the –numprocesses flag

pytest –numprocesses auto           

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

有關 pytest 選項的常用資訊,請參考 Running Tests。

示例

配置 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(“https://example.com”)

# …           

在特定的浏覽器上運作測試

# conftest.py

import pytest

@pytest.mark.only_browser(“chromium”)

def test_visit_example(page):

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

# …           

使用自定義的浏覽器通道運作

pytest –browser-channel chrome

# test_my_application.py

def test_example(page):

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

配置 base-url

使用 base-url 參數啟動 Pytest。pytest-base-url (https://github.com/pytest-dev/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           

調試

使用 pdb

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

def test_bing_is_working(page):

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

breakpoint()

# …           

部署到 CI

請檢視 guides for CI providers 擷取關于将測試部署到 CI/CD 的資訊。

認證

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

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

注意:本指南覆寫 cookie/token-based 認證(通過 app UI 登陸)。對于 HTTP 認證(HTTP authentication:https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication),請使用 browser.new_context(**kwargs):https://playwright.dev/python/docs/api/class-browser#browser-new-context。

自動化登入

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           

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

重用簽入狀态

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

Web 應用使用基于 Cookie 或基于 Token 的認證,認證狀态被當作 Cookie 存儲,或被存儲在 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”)           

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.com’) {

const entries = JSON.parse(storage)

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

window.sessionStorage.setItem(key, value)

}

}

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

多因子認證

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

持久化認證

注意:持久化認證不适用于 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. 重用使用者資料目錄來運作自動化場景

事件

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

等待事件

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

使用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”)           

添加/移除事件監聽者

有時,事件發生在随機時間,而不是等待它們,它們需要被處理。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/”)           

添加一次性監聽者

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

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

page.evaluate(“prompt(‘Enter a number:’)”)           

API 測試

Playwright 可用于通路應用程式的 REST API。

有時,你可能想通過 Python 直接向服務端發送請求,而非加載頁面,在其中運作 js 代碼。此時,Playwright 可以派上用場的一些示例如下:

  1. 測試服務端 API
  2. 在通路 Web 應用程式之前,準備服務端狀态
  3. 在浏覽器中運作一些操作後,驗證服務端的後置條件

這些都可以通過 APIRequestContext 方法實作。

下面的示例依賴 pytest-playwright 包,它向 Pytest test-runner 添加 Playwright fixture。

  1. Writing API Test
  2. Prepare server state via API calls
  3. Check the server state after running user actions
  4. Reuse authentication state

編寫 API 測試

APIRequestContext 可以通過網絡發送各種 HTTP(S) 請求。

下面的示例展示如何使用 Playwright 通過 GitHub API 測試 issue 建立。測試套件做如下事情:

  1. 在運作測試之前,建立新倉庫
  2. 建立一些 issue 及驗證服務端狀态
  3. 在運作測試後删除倉庫

配置

Github API 需要認證,是以我們将為所有測試配置一次 token。同時,我們也将設定 baseURL 以簡化測試。

import os

from typing import Generator

import pytest

from playwright.sync_api import Playwright, APIRequestContext

GITHUB_API_TOKEN = os.getenv(“GITHUB_API_TOKEN”)

assert GITHUB_API_TOKEN, “GITHUB_API_TOKEN is not set”

@pytest.fixture(scope=”session”)

def api_request_context(

playwright: Playwright,

) -> Generator[APIRequestContext, None, None]:

headers = {

# We set this header per GitHub guidelines.

“Accept”: “application/vnd.github.v3+json”,

# Add authorization token to all requests.

# Assuming personal access token available in the environment.

“Authorization”: f”token {GITHUB_API_TOKEN}”,

}

request_context = playwright.request.new_context(

base_url=”https://api.github.com”, extra_http_headers=headers

)

yield request_context

request_context.dispose()           

編寫測試

現在我們已初始化請求對象,我們可以添加一些在倉庫中建立新 issue 的測試。

import os

from typing import Generator

import pytest

from playwright.sync_api import Playwright, APIRequestContext

GITHUB_API_TOKEN = os.getenv(“GITHUB_API_TOKEN”)

assert GITHUB_API_TOKEN, “GITHUB_API_TOKEN is not set”

GITHUB_USER = os.getenv(“GITHUB_USER”)

assert GITHUB_USER, “GITHUB_USER is not set”

GITHUB_REPO = “test”

# …

def test_should_create_bug_report(api_request_context: APIRequestContext) -> None:

data = {

“title”: “[Bug] report 1”,

“body”: “Bug description”,

}

new_issue = api_request_context.post(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”, data=data)

assert new_issue.ok

issues = api_request_context.get(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”)

assert issues.ok

issues_response = issues.json()

issue = list(filter(lambda issue: issue[“title”] == “[Bug] report 1”, issues_response))[0]

assert issue

assert issue[“body”] == “Bug description”

def test_should_create_feature_request(api_request_context: APIRequestContext) -> None:

data = {

“title”: “[Feature] request 1”,

“body”: “Feature description”,

}

new_issue = api_request_context.post(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”, data=data)

assert new_issue.ok

issues = api_request_context.get(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”)

assert issues.ok

issues_response = issues.json()

issue = list(filter(lambda issue: issue[“title”] == “[Feature] request 1”, issues_response))[0]

assert issue

assert issue[“body”] == “Feature description”           

準備(setup)和銷毀(teardown)

這些測試假定倉庫存在。你可能想在運作測試之前建立新倉庫,之後删除它。為此使用 session fixture。yield 之前的部分在所有測試之前執行,之後的部分在所有測試之後執行。

# …

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

def create_test_repository(

api_request_context: APIRequestContext,

) -> Generator[None, None, None]:

# Before all

new_repo = api_request_context.post(“/user/repos”, data={“name”: GITHUB_REPO})

assert new_repo.ok

yield

# After all

deleted_repo = api_request_context.delete(f”/repos/{GITHUB_USER}/{GITHUB_REPO}”)

assert deleted_repo.ok           

完成樣例測試

這是 API 測試的完整樣例:

from enum import auto

import os

from typing import Generator

import pytest

from playwright.sync_api import Playwright, Page, APIRequestContext, expect

GITHUB_API_TOKEN = os.getenv(“GITHUB_API_TOKEN”)

assert GITHUB_API_TOKEN, “GITHUB_API_TOKEN is not set”

GITHUB_USER = os.getenv(“GITHUB_USER”)

assert GITHUB_USER, “GITHUB_USER is not set”

GITHUB_REPO = “test”

@pytest.fixture(scope=”session”)

def api_request_context(

playwright: Playwright,

) -> Generator[APIRequestContext, None, None]:

headers = {

# We set this header per GitHub guidelines.

“Accept”: “application/vnd.github.v3+json”,

# Add authorization token to all requests.

# Assuming personal access token available in the environment.

“Authorization”: f”token {GITHUB_API_TOKEN}”,

}

request_context = playwright.request.new_context(

base_url=”https://api.github.com”, extra_http_headers=headers

)

yield request_context

request_context.dispose()

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

def create_test_repository(

api_request_context: APIRequestContext,

) -> Generator[None, None, None]:

# Before all

new_repo = api_request_context.post(“/user/repos”, data={“name”: GITHUB_REPO})

assert new_repo.ok

yield

# After all

deleted_repo = api_request_context.delete(f”/repos/{GITHUB_USER}/{GITHUB_REPO}”)

assert deleted_repo.ok

def test_should_create_bug_report(api_request_context: APIRequestContext) -> None:

data = {

“title”: “[Bug] report 1”,

“body”: “Bug description”,

}

new_issue = api_request_context.post(

f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”, data=data

)

assert new_issue.ok

issues = api_request_context.get(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”)

assert issues.ok

issues_response = issues.json()

issue = list(

filter(lambda issue: issue[“title”] == “[Bug] report 1”, issues_response)

)[0]

assert issue

assert issue[“body”] == “Bug description”

def test_should_create_feature_request(api_request_context: APIRequestContext) -> None:

data = {

“title”: “[Feature] request 1”,

“body”: “Feature description”,

}

new_issue = api_request_context.post(

f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”, data=data

)

assert new_issue.ok

issues = api_request_context.get(f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”)

assert issues.ok

issues_response = issues.json()

issue = list(

filter(lambda issue: issue[“title”] == “[Feature] request 1”, issues_response)

)[0]

assert issue

assert issue[“body”] == “Feature description”           

通過 API 調用準備服務端狀态

下面的測試通過 API 建立新 issue,然後導航到項目中所有 issue 的清單,檢查它是否出現在清單的頂部。使用LocatorAssertions執行該檢查。

def test_last_created_issue_should_be_first_in_the_list(api_request_context: APIRequestContext, page: Page) -> None:

def create_issue(title: str) -> None:

data = {

“title”: title,

“body”: “Feature description”,

}

new_issue = api_request_context.post(

f”/repos/{GITHUB_USER}/{GITHUB_REPO}/issues”, data=data

)

assert new_issue.ok

create_issue(“[Feature] request 1”)

create_issue(“[Feature] request 2”)

page.goto(f”https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues”)

first_issue = page.locator(“a[data-hovercard-type=’issue’]”).first

expect(first_issue).to_have_text(“[Feature] request 2”)           

在運作使用者操作後,檢查伺服器狀态

下面的測試通過浏覽器中的使用者接口建立新 issue,然後通過 API 檢查它是否被建立。

def test_last_created_issue_should_be_on_the_server(api_request_context: APIRequestContext, page: Page) -> None:

page.goto(f”https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues”)

page.locator(“text=New issue”).click()

page.locator(“[aria-label=’Title’]”).fill(“Bug report 1”)

page.locator(“[aria-label=’Comment body’]”).fill(“Bug description”)

page.locator(“text=Submit new issue”).click()

issue_id = page.url.split(“/”)[-1]

new_issue = api_request_context.get(f”https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues/{issue_id}”)

assert new_issue.ok

assert new_issue.json()[“title”] == “[Bug] report 1”

assert new_issue.json()[“body”] == “Bug description”           

重用認證狀态

Web 應用程式使用基于 Cookie 或基于 Token 的認證,認證狀态被存儲為 Cookies。Playwright 提供api_request_context.storage_state(**kwargs)方法,它可用于從已認證上下文中擷取存儲狀态,然後使用該狀态建立新上下文。

BrowserContext和APIRequestContext之間可交換存儲狀态。你可以通過 API 調用登入,然後使用已有的 Cookie 建立新上下文。下面的代碼片段從已認證APIRequestContext擷取狀态,然後使用該狀态建立新浏覽器上下文(BrowserContext)。

request_context = playwright.request.new_context(http_credentials={“username”: “test”, “password”: “test”})

request_context.get(“https://api.example.com/login”)

# Save storage state into a variable.

state = request_context.storage_state()

# Create a new context with the saved storage state.

context = browser.new_context(storage_state=state)           

from https://www.anquanke.com/post/id/286627

繼續閱讀