安装

引言

Playwright 是专门为满足端到端测试的需求而创建的。Playwright 支持包括 Chromium、WebKit 和 Firefox 在内的所有现代渲染引擎。在 Windows、Linux 和 macOS 上进行测试,无论是本地还是在 CI 上,无论是无头模式还是有头模式,都可以使用原生移动设备仿真。

Playwright 库可以用作通用的浏览器自动化工具,为同步和异步 Python 提供了一套强大的 API 来自动化 Web 应用程序。

本介绍描述了 Playwright Pytest 插件,这是编写端到端测试的推荐方式。

你将学到

  • 如何安装 Playwright Pytest
  • 如何运行示例测试

安装 Playwright Pytest

Playwright 建议使用官方的 Playwright Pytest 插件来编写端到端测试。它提供了上下文隔离,并开箱即用地在多个浏览器配置上运行。

首先,通过安装 Playwright 并运行示例测试来开始,亲眼看看它是如何工作的。

  • PyPI
  • Anaconda

安装所需的浏览器:

添加示例测试

在当前工作目录或遵循 test_ 前缀约定的子目录中创建一个文件,例如 test_example.py,并在其中包含以下代码。确保你的测试名称也遵循 test_ 前缀约定。

test_example.py

import re
from playwright.sync_api import Page, expect

def test_has_title(page: Page):
    page.goto("https://playwright.dev/")

    # 期望标题“包含”一个子字符串。
    expect(page).to_have_title(re.compile("Playwright"))

def test_get_started_link(page: Page):
    page.goto("https://playwright.dev/")

    # 点击“开始”链接。
    page.get_by_role("link", name="Get started").click()

    # 期望页面有一个名为“安装”的标题。
    expect(page.get_by_role("heading", name="Installation")).to_be_visible()

运行示例测试

默认情况下,测试将在 Chromium 上运行。这可以通过 CLI 选项进行配置。测试以无头模式运行,这意味着在运行测试时不会打开浏览器 UI。测试结果和测试日志将在终端中显示。

更新 Playwright

要将 Playwright 更新到最新版本,请运行以下命令:

pip install pytest-playwright playwright -U

系统要求

  • Python 3.8 或更高版本。
  • Windows 10+、Windows Server 2016+ 或 Windows Subsystem for Linux (WSL)。
  • MacOS 12 Monterey、MacOS 13 Ventura 或 MacOS 14 Sonoma。
  • Debian 11、Debian 12、Ubuntu 20.04 或 Ubuntu 22.04。

接下来是什么

  • 使用 Web 优先断言、页面夹具和定位器编写测试
  • 运行单个测试、多个测试、有头模式
  • 使用 Codegen 生成测试
  • 查看测试的跟踪

编写测试用例

引言

Playwright 测试很简单,它们

  • 执行操作
  • 断言状态与预期相符。

在执行操作之前没有必要等待任何东西:Playwright 在执行每个操作之前会自动等待一系列可操作性检查通过。

也没有必要在执行检查时处理竞态条件 - Playwright 断言的设计方式是它们描述了最终需要满足的预期。

就是这样!这些设计选择允许 Playwright 用户完全忘记他们测试中的不稳定超时和竞态检查。

你将学到

  • 如何编写第一个测试
  • 如何执行操作
  • 如何使用断言
  • 测试如何独立运行
  • 如何使用测试钩子

第一个测试

查看以下示例,了解如何编写测试。注意文件名遵循 test_ 前缀约定,以及每个测试名称。

test_example.py

import re
from playwright.sync_api import Page, expect

def test_has_title(page: Page):
    page.goto("https://playwright.dev/")

    # 期望标题“包含”一个子字符串。
    expect(page).to_have_title(re.compile("Playwright"))

def test_get_started_link(page: Page):
    page.goto("https://playwright.dev/")

    # 点击“开始”链接。
    page.get_by_role("link", name="Get started").click()

    # 期望页面有一个名为“安装”的标题。
    expect(page.get_by_role("heading", name="Installation")).to_be_visible()

操作

导航

大多数测试将从导航到 URL 的页面开始。之后,测试将能够与页面元素进行交互。

page.goto("https://playwright.dev/")

Playwright 会等待页面达到加载状态,然后再继续。了解更多关于 page.goto() 选项的信息。

交互

执行操作从定位元素开始。Playwright 使用 Locators API 进行此操作。定位器代表了在任何时刻在页面上查找元素(或元素组)的方式,了解更多关于可用的不同类型定位器的信息。Playwright 会在执行操作之前等待元素可操作,因此没有必要等待其变为可用。

# 创建一个定位器。
get_started = page.get_by_role("link", name="Get started")

# 点击它。
get_started.click()

在大多数情况下,它会写成一行:

page.get_by_role("link", name="Get started").click()

基本操作

这是最受欢迎的 Playwright 操作列表。请注意,还有更多操作,请确保查看定位器 API 部分以了解更多信息。

操作描述
locator.check()检查输入复选框
locator.click()点击元素
locator.uncheck()取消检查输入复选框
locator.hover()鼠标悬停在元素上
locator.fill()填写表单字段,输入文本
locator.focus()使元素获得焦点
locator.press()按下单个键
locator.set_input_files()选择上传的文件
locator.select_option()在下拉菜单中选择选项

断言

Playwright 包括断言,这些断言会等待直到满足预期条件。使用这些断言可以使测试不脆弱且具有弹性。例如,这段代码会等待页面获得包含“Playwright”的标题:

import re
from playwright.sync_api import expect

expect(page).to_have_title(re.compile("Playwright"))

这是最受欢迎的异步断言列表。请注意,还有更多断言需要熟悉:

断言描述
expect(locator).to_be_checked()复选框被选中
expect(locator).to_be_enabled()控件被启用
expect(locator).to_be_visible()元素可见
expect(locator).to_contain_text()元素包含文本
expect(locator).to_have_attribute()元素具有属性
expect(locator).to_have_count()元素列表具有给定长度
expect(locator).to_have_text()元素匹配文本``
expect(locator).to_have_value()输入元素具有值``
expect(page).to_have_title()页面具有标题``
expect(page).to_have_url()页面具有 URL

测试隔离

Playwright Pytest 插件基于测试夹具的概念,例如内置的页面夹具,它被传递到你的测试中。由于浏览器上下文,页面在测试之间是隔离的,这相当于一个全新的浏览器配置文件,每个测试都获得一个新鲜环境,即使多个测试在单个浏览器中运行。

test_example.py

from playwright.sync_api import Page

def test_example_test(page: Page):
  pass
  # “page”属于一个独立的 BrowserContext,为这个特定测试创建。

def test_another_test(page: Page):
  pass
  # 这个第二个测试中的“page”与第一个测试完全隔离。

使用夹具

你可以使用各种夹具在测试之前或之后执行代码并共享对象。一个 function 作用域的夹具,例如带有 autouse 的,表现得像 beforeEach/afterEach。一个 module 作用域的夹具带有 autouse 表现得像 beforeAll/afterAll,它在所有测试之前和之后运行。

test_example.py

import pytest
from playwright.sync_api import Page, expect

@pytest.fixture(scope="function", autouse=True)
def before_each_after_each(page: Page):

    print("before the test runs")

    # 在每个测试之前转到起始 URL。
    page.goto("https://playwright.dev/")
    yield

    print("after the test runs")

def test_main_navigation(page: Page):
    # 断言使用 expect API。
    expect(page).to_have_url("https://playwright.dev/")

接下来是什么

  • 运行单个测试,多个测试,有头模式
  • 使用 Codegen 生成测试
  • 查看测试的跟踪
  • 在 CI 上运行测试与 GitHub Actions

生成测试

引言

Playwright 提供了开箱即用的测试生成能力,是快速开始测试的绝佳方式。它将打开两个窗口,一个浏览器窗口,您可以在其中与您想要测试的网站进行交互;另一个是 Playwright 检查器窗口,您可以在其中录制测试、复制测试、清除测试以及更改测试的语言。

您将学到

  • 如何录制测试
  • 如何生成定位器

运行 Codegen

使用 codegen 命令运行测试生成器,然后是您想要为其生成测试的网站的 URL。URL 是可选的,您也可以在不指定 URL 的情况下运行命令,然后在浏览器窗口中直接添加 URL。

playwright codegen demo.playwright.dev/todomvc

录制测试

运行 codegen 并在浏览器中执行操作。Playwright 将为用户交互生成代码。Codegen 将查看渲染后的页面并找出推荐的定位器,优先考虑角色、文本和测试 ID 定位器。如果生成器识别出多个元素与定位器匹配,它将改进定位器,使其更具弹性并唯一地识别目标元素,从而消除和减少由于定位器导致的测试失败和不稳定。

使用测试生成器,您可以录制:

  • 通过简单地与页面交互,执行点击或填充等操作
  • 通过点击工具栏上的一个图标,然后点击页面上的元素进行断言。您可以选择:
    • 'assert visibility' 断言元素可见
    • 'assert text' 断言元素包含特定文本
    • 'assert value' 断言元素具有特定值

当您完成与页面的交互后,按下 'record' 按钮停止录制,并使用 'copy' 按钮将生成的代码复制到您的编辑器中。

使用 'clear' 按钮清除代码以开始重新录制。完成后,关闭 Playwright 检查器窗口或停止终端命令。

要了解更多关于生成测试的信息,请查看我们关于 Codegen 的详细指南。

生成定位器

您可以使用测试生成器生成定位器。

  • 按下 'Record' 按钮停止录制,‘Pick Locator’` 按钮将会出现。
  • 点击 'Pick Locator' 按钮,然后将鼠标悬停在浏览器窗口的元素上,以查看定位器在每个元素下方突出显示。
  • 要选择一个定位器,单击您想要定位的元素,该定位器的代码将出现在 Pick Locator 按钮旁边的定位器游乐场中。
  • 然后您可以在定位器游乐场中编辑定位器以微调它,并看到匹配的元素在浏览器窗口中突出显示。
  • 使用复制按钮复制定位器并将其粘贴到您的代码中。

仿真

您还可以使用仿真生成测试,以便为特定的视口、设备、颜色方案生成测试,以及模拟地理位置、语言或时区。测试生成器还可以在保持认证状态的同时生成测试。查看测试生成器指南以了解更多信息。

接下来做什么

  • 查看您的测试跟踪

运行和调试

引言

您可以运行单个测试、一组测试或所有测试。使用 --browser 标志,可以在一个浏览器或多个浏览器上运行测试。默认情况下,测试以无头模式运行,这意味着在运行测试时不会打开浏览器窗口,结果将在终端中显示。如果您愿意,可以通过使用 --headed CLI 参数以有头模式运行您的测试。

您将学到

  • 如何从命令行运行测试
  • 如何调试测试

运行测试

命令行

要运行您的测试,请使用 pytest 命令。这将默认在 Chromium 浏览器上运行您的测试。默认情况下,测试以无头模式运行,这意味着在运行测试时不会打开浏览器窗口,结果将在终端中显示。

在有头模式下运行测试

要以有头模式运行您的测试,请使用 --headed 标志。这将在运行测试时打开浏览器窗口,并在完成后关闭浏览器窗口。

在不同的浏览器上运行测试

要指定您想要运行测试的浏览器,请使用 --browser 标志,后跟浏览器的名称。

要指定要在多个浏览器上运行测试,请多次使用 --browser 标志,后跟每个浏览器的名称。

pytest --browser webkit --browser firefox

运行特定测试

要运行单个测试文件,请传入您想要运行的测试文件的名称。

要运行一组测试文件,请传入您想要运行的测试文件的名称。

pytest tests/test_todo_page.py tests/test_landing_page.py

要运行特定测试,请传入您想要运行的测试的函数名称。

pytest -k test_add_a_todo_item

并行运行测试

要并行运行您的测试,请使用 --numprocesses 标志,后跟您想要运行测试的进程数量。我们建议使用逻辑 CPU 核心数的一半。

(这假设已安装了 pytest-xdist。有关更多信息,请参见此处。)

有关更多信息,请参见 Playwright Pytest 使用情况或 Pytest 文档以了解一般 CLI 使用情况。

调试测试

由于 Playwright 在 Python 中运行,您可以使用您选择的调试器进行调试,例如使用 Visual Studio Code 中的 Python 扩展。Playwright 带有 Playwright 检查器,允许您逐步执行 Playwright API 调用,查看它们的调试日志并探索定位器。

要调试所有测试,请运行以下命令。

  • Bash
  • PowerShell
  • Batch

要调试一个测试文件,请在命令后运行您想要调试的测试文件的名称。

  • Bash
  • PowerShell
  • Batch
PWDEBUG=1 pytest -s test_example.py

要调试特定测试,请添加 -k 后跟您想要调试的测试的名称。

  • Bash
  • PowerShell
  • Batch
PWDEBUG=1 pytest -s -k test_get_started_link

此命令将打开浏览器窗口以及 Playwright 检查器。您可以使用检查器顶部的步过按钮逐步执行您的测试。或者按下播放按钮从开始到结束运行您的测试。一旦测试完成,浏览器窗口将关闭。

在调试时,您可以使用选择定位器按钮来选择页面上的元素,并查看 Playwright 用于查找该元素的定位器。您还可以编辑定位器并实时在浏览器窗口中看到它被高亮显示。使用复制定位器按钮将定位器复制到剪贴板,然后将其粘贴到您的测试中。

查看我们的调试指南,以了解更多关于 Playwright 检查器以及使用浏览器开发工具进行调试的信息。

接下来做什么

  • 使用 Codegen 生成测试
  • 查看您的测试跟踪
  • 在 CI 上使用 GitHub Actions 运行您的测试

跟踪查看器

介绍

Playwright Trace Viewer 是一个图形用户界面(GUI)工具,允许您探索录制的 Playwright 测试跟踪,这意味着您可以前后浏览测试的每个动作,并直观地看到每个动作期间发生了什么。

您将学到

  • 如何录制跟踪
  • 如何打开 HTML 报告
  • 如何打开跟踪查看器

录制跟踪

可以通过使用 --tracing 标志运行测试来录制跟踪。

跟踪的选项有:

  • on: 为每个测试录制跟踪
  • off: 不录制跟踪。(默认)
  • retain-on-failure: 为每个测试录制跟踪,但删除成功测试运行的所有跟踪。

这将录制跟踪并将其放入 test-results 目录中名为 trace.zip 的文件中。

如果您没有使用 Pytest,请点击这里了解如何录制跟踪。

  • 同步
  • 异步
browser = chromium.launch()
context = browser.new_context()

# 在创建/导航页面之前开始跟踪。
context.tracing.start(screenshots=True, snapshots=True, sources=True)
page = context.new_page()
page.goto("https://playwright.dev")

# 停止跟踪并将其导出到 zip 归档中。
context.tracing.stop(path="trace.zip")

打开跟踪

您可以使用 Playwright CLI 或在 trace.playwright.dev 上的浏览器中打开保存的跟踪。确保添加您的 trace.zip 文件所在的完整路径。这应该包括 test-results 目录,然后是测试名称,最后是 trace.zip

playwright show-trace trace.zip

查看跟踪

通过点击每个动作或使用时间线悬停来查看测试的跟踪,并查看动作前后页面的状态。在测试的每个步骤期间检查日志、源代码和网络。跟踪查看器创建了一个 DOM 快照,因此您可以完全与之交互,打开开发工具等。

要了解更多信息,请查看我们关于跟踪查看器的详细指南。

接下来是什么

  • 在 GitHub Actions 上运行 CI 测试
  • 了解更多关于跟踪查看器的信息

Pytest 插件来编写端到端测试

引言

Playwright 提供了一个 Pytest 插件来编写端到端测试。要开始使用它,请参考入门指南。

使用方法

要运行测试,请使用 Pytest 命令行界面。

pytest --browser webkit --headed

如果你想自动添加命令行参数而不需要明确指定它们,你可以使用 pytest.ini 文件:

# pytest.ini 文件的内容
[pytest]
# 使用 UI 运行 firefox
addopts = --headed --browser firefox

命令行参数

请注意,命令行参数仅应用于默认的 browsercontextpage 固定装置。如果你使用 API 调用如 browser.new_context() 创建浏览器、上下文或页面,命令行参数将不会应用。

  • --headed:以有头模式运行测试(默认:无头)。
  • --browser:在不同的浏览器中运行测试 chromiumfirefoxwebkit。可以多次指定(默认:chromium)。
  • --browser-channel:要使用的浏览器渠道。
  • --slowmo:通过指定的毫秒数减慢 Playwright 操作。这样你可以看清楚发生了什么(默认:0)。
  • --device:要模拟的设备。
  • --output:由测试产生的工件目录(默认:test-results)。
  • --tracing:是否为每个测试记录跟踪。onoffretain-on-failure(默认:off)。
  • --video:是否为每个测试录制视频。onoffretain-on-failure(默认:off)。
  • --screenshot:是否在每个测试后自动捕获屏幕截图。onoffonly-on-failure(默认:off)。
  • --full-page-screenshot:在失败时是否拍摄整个页面的屏幕截图。默认情况下,只捕获视口。需要启用 --screenshot(默认:off)。

固定装置

这个插件为 pytest 配置了 Playwright 特定的固定装置。要使用这些固定装置,将固定装置名称作为参数传递给测试函数。

def test_my_app_is_working(fixture_name):
    pass
    # 使用 fixture_name 进行测试
    # ...

函数范围 :当在测试函数中请求时,这些固定装置被创建,并在测试结束时销毁。

  • context:测试的新浏览器上下文。
  • page:测试的新浏览器页面。
  • new_context:允许为测试创建不同的浏览器上下文。适用于多用户场景。接受与 browser.new_context() 相同的参数。

会话范围 :当在测试函数中请求时,这些固定装置被创建,并在所有测试结束时销毁。

  • playwright:Playwright 实例。
  • browser_type:当前浏览器的 BrowserType 实例。
  • browser:由 Playwright 启动的浏览器实例。
  • browser_name:浏览器名称字符串。
  • browser_channel:浏览器渠道字符串。
  • is_chromiumis_webkitis_firefox:相应浏览器类型的布尔值。

自定义固定装置选项 :对于 browsercontext 固定装置,使用以下固定装置定义自定义启动选项。

  • browser_type_launch_args:覆盖 browser_type.launch() 的启动参数。它应该返回一个字典。
  • browser_context_args:覆盖 browser.new_context() 的选项。它应该返回一个字典。

也可以通过使用 browser_context_args 标记来覆盖单个测试的上下文选项(browser.new_context()):

import pytest

@pytest.mark.browser_context_args(timezone_id="Europe/Berlin", locale="en-GB")
def test_browser_context_args(page):
    assert page.evaluate("window.navigator.userAgent") == "Europe/Berlin"
    assert page.evaluate("window.navigator.languages") == ["de-DE"]

并行性:同时运行多个测试

如果你的测试在拥有大量 CPU 的机器上运行,你可以通过使用 pytest-xdist 同时运行多个测试来加快测试套件的整体执行时间:

# 安装依赖
pip install pytest-xdist
# 使用 --numprocesses 标志
pytest --numprocesses auto

根据硬件和测试的性质,你可以将 numprocesses 设置为从 2 到机器上的 CPU 数量。如果设置得太高,你可能会注意到意外的行为。

有关 pytest 选项的一般信息,请参见运行测试。

示例

为自动补全配置 Mypy 类型

test_my_application.py

from playwright.sync_api import Page

def test_visit_admin_dashboard(page: Page):
    page.goto("/admin")
    # ...

配置慢动作

使用 --slowmo 参数运行测试。

将 Playwright 操作减慢 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")
    # ...

使用自定义浏览器渠道,如 Google Chrome 或 Microsoft Edge

pytest --browser-channel chrome

test_my_application.py

def test_example(page):
    page.goto("https://example.com")

配置基础 URL

使用 base-url 参数启动 Pytest。pytest-base-url 插件用于此目的,它允许你从配置、命令行参数或固定装置中设置基础 URL。

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

test_my_application.py

def test_visit_example(page):
    page.goto("/admin")
    # -> 将导致 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"

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

请参阅 CI 提供商的指南,了解如何将你的测试部署到 CI/CD。