无论您选择什么身份验证策略,您很可能会将经过身份验证的浏览器状态存储在文件系统中。
我们建议创建 playwright/.auth
目录并将其添加到 .gitignore
文件中。您的身份验证程序将生成经过身份验证的浏览器状态并将其保存到该 playwright/.auth
目录中的文件中。稍后,测试将重用此状态并开始已经经过身份验证的状态。
mkdir -p playwright/.auth
echo "\nplaywright/.auth" >> .gitignore
New-Item -ItemType Directory -Force -Path playwright\.auth
Add-Content -path .gitignore "`r`nplaywright/.auth"
md playwright\.auth
echo. >> .gitignore
echo "playwright/.auth" >> .gitignore
Playwright API可以自动化与登录表单的交互。
为每个测试重新执行登录可能会降低测试的执行速度。为了缓解这个问题,可以重复使用现有的身份验证状态。
下面是一个注册流程的示例:
# test_demo.py
from playwright.sync_api import Page, expect
from faker import Faker
import re
faker = Faker()
import random
def test_example(page: Page) -> None:
page.goto("https://www.gitlink.org.cn")
page.locator(selector="//a[text()='登录']/following-sibling::a").click()
page.get_by_text("邮箱注册").click()
page.locator(selector="//input[@id='register_register_username']").fill(f"glcc_{random.randint(1, 100)}")
page.get_by_placeholder("请输入邮箱地址").fill(f"glcc_{random.randint(1, 100)}@gitlink.com")
page.get_by_placeholder("请输入验证码").fill("123123")
page.get_by_placeholder("请输入登录密码").fill("12345678")
page.get_by_placeholder("请确认登录密码").fill("12345678")
page.get_by_label("我已阅读并接受《GitLink服务协议条款》").check()
page.locator("button").filter(has_text="注 册").click()
expect(page, "检查用户注册后是否跳转到个人主页").to_have_title(re.compile("glcc_"), timeout=5_000)
# main.py
import pytest
pytest.main(['--headed', '--browser=chromium', "--browser-channel=chrome"])
Playwright 提供了一种在测试中重复使用已登录状态的方式。这样你就可以只登录一次,然后在所有测试中跳过登录步骤。
Web 应用程序使用基于 cookie 或令牌的身份验证,已认证的状态存储在 cookie 或本地存储中。Playwright 提供了 browserContext.storageState([options])
方法,用于从已认证的上下文中检索存储状态,并创建具有预填充状态的新上下文。
Cookies 和本地存储状态可以跨不同的浏览器使用,具体取决于应用程序的身份验证模型:某些应用程序可能同时需要 cookies 和本地存储。
以下代码片段检索来已认证上下文的状态,并使用该状态创建一个新的上下文。
利用其他测试方法中的登录态。
# test_demo.py
from playwright.sync_api import Page, expect
from faker import Faker
import re
faker = Faker()
import random
# 创建一个全局变量来保存存储状态
storage = None
def test_register(page: Page) -> None:
global storage
page.goto("https://www.gitlink.org.cn")
page.locator(selector="//a[text()='登录']/following-sibling::a").click()
page.get_by_text("邮箱注册").click()
page.locator(selector="//input[@id='register_register_username']").fill(f"glcc_{random.randint(1, 100)}")
page.get_by_placeholder("请输入邮箱地址").fill(f"glcc_23{random.randint(1, 100)}@gitlink.com")
page.get_by_placeholder("请输入验证码").fill("123123")
page.get_by_placeholder("请输入登录密码").fill("12345678")
page.get_by_placeholder("请确认登录密码").fill("12345678")
page.get_by_label("我已阅读并接受《GitLink服务协议条款》").check()
page.locator("button").filter(has_text="注 册").click()
expect(page, "检查用户注册后是否跳转到个人主页").to_have_title(re.compile("glcc_"), timeout=5_000)
# 登录成功后获取当前上下文的存储状态,并存储在文件state.json中
storage = page.context.storage_state(path="state.json")
# 在其他测试中,创建新的上下文并使用之前保存的存储状态
def test_new_project(page: Page) -> None:
global storage
"""
第1种写法
创建新的上下文,使用之前存储的状态文件state.json
# new_context = page.context.browser.new_context(storage_state=storage)
# new_page = new_context.new_page()
"""
"""
第2种写法
直接新建一个页面,使用之前存储的状态文件state.json
"""
new_page = page.context.browser.new_page(storage_state=storage)
# TODO 这两种方式上,虽然用例可以执行成功,但是新开了好几个浏览器窗口
# 执行其他测试操作(此时是登录状态)
new_page.goto("https://www.gitlink.org.cn")
new_page.locator("#nHeader img").nth(1).click()
new_page.get_by_text("新建项目").click()
expect(new_page, "检查是否跳转到了新建项目页面").to_have_title(re.compile("新建项目"), timeout=5_000)
# main.py
import pytest
pytest.main(['--headed', '--browser=chromium', "--browser-channel=chrome"])
将登录态写在contest.py
中,作为fixture。
# contest.py
import pytest
import re
from playwright.sync_api import Page, expect
# 定义全局登录
@pytest.fixture(scope="function", autouse=True)
def page(page: Page):
page.goto("https://www.gitlink.org.cn/login")
page.locator(selector="//input[@id='login_username']").fill("chytest10")
page.locator(selector="//input[@id='login_password']").fill("12345678")
page.locator("span").filter(has_text="登 录").click()
expect(page, "检查用户登录后是否跳转到个人主页").to_have_title(re.compile("chytest10"), timeout=5_000)
# 登录成功后获取当前上下文的存储状态,并存储在文件state.json中
storage = page.context.storage_state(path="state.json")
new_page = page.context.browser.new_page(storage_state=storage)
yield new_page
# test_demo.py
from playwright.sync_api import Page, expect
import re
def test_new_project_02(page: Page) -> None:
page.goto("https://www.gitlink.org.cn")
page.locator("#nHeader img").nth(1).click()
page.get_by_text("新建项目").click()
expect(page, "检查是否跳转到了新建项目页面").to_have_title(re.compile("新建项目"), timeout=5_000)
# main.py
import pytest
pytest.main(['--headed', '--browser=chromium', "--browser-channel=chrome"])
重用身份验证状态涵盖了基于Cookie和本地存储的身份验证。很少情况下,会使用Session存储来存储与登录状态相关的信息。Session存储是针对特定域的,并且在页面加载时不会被保留。Playwright没有提供API来持久化Session存储,但可以使用以下代码片段来保存/加载Session存储。
# contest.py
import pytest
import re
import os
from playwright.sync_api import Page, expect
# 定义全局登录
@pytest.fixture(scope="function", autouse=True)
def page(page: Page):
page.goto("https://www.gitlink.org.cn/login")
page.locator(selector="//input[@id='login_username']").fill("chytest10")
page.locator(selector="//input[@id='login_password']").fill("12345678")
page.locator("span").filter(has_text="登 录").click()
expect(page, "检查用户登录后是否跳转到个人主页").to_have_title(re.compile("chytest10"), timeout=5_000)
# 获取当前页面的会话存储
session_storage = page.evaluate("() => JSON.stringify(sessionStorage)")
# 将会话存储存储为环境变量
os.environ["SESSION_STORAGE"] = session_storage
session_storage = os.environ["SESSION_STORAGE"]
new_context = page.context
new_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 + "')")
new_page = new_context.new_page()
yield new_page
# test_demo.py
from playwright.sync_api import Page, expect
import re
def test_new_project_02(page: Page) -> None:
page.goto("https://www.gitlink.org.cn")
page.locator("#nHeader img").nth(1).click()
page.get_by_text("新建项目").click()
expect(page, "检查是否跳转到了新建项目页面").to_have_title(re.compile("新建项目"), timeout=5_000)
# main.py
import pytest
pytest.main(['--headed', '--browser=chromium', "--browser-channel=chrome"])