Playwright for Python:鉴权Authentication


Playwright 在称为浏览器上下文的隔离环境中执行测试。这种隔离模型提高了可重现性,并防止了级联测试失败。测试可以加载现有的身份验证状态。这消除了在每个测试中进行身份验证的需求,并加快了测试的执行速度。

核心概念

无论您选择什么身份验证策略,您很可能会将经过身份验证的浏览器状态存储在文件系统中。

我们建议创建 playwright/.auth 目录并将其添加到 .gitignore 文件中。您的身份验证程序将生成经过身份验证的浏览器状态并将其保存到该 playwright/.auth 目录中的文件中。稍后,测试将重用此状态并开始已经经过身份验证的状态。

Bash

mkdir -p playwright/.auth
echo "\nplaywright/.auth" >> .gitignore

PowerShell

New-Item -ItemType Directory -Force -Path playwright\.auth
Add-Content -path .gitignore "`r`nplaywright/.auth"

Batch

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 和本地存储。

以下代码片段检索来已认证上下文的状态,并使用该状态创建一个新的上下文。

示例1

利用其他测试方法中的登录态。

# 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"])

示例2

将登录态写在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"])

猜你喜欢

转载自blog.csdn.net/FloraCHY/article/details/132711033