You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在pytest-playwright与Django中复用测试登录状态?

问题分析与解决方案

你的代码存在几个核心问题导致登录状态无法复用:

  1. logged_in fixture中邮箱拼写错误(bob@exmpale.com少了字母l),实际未完成有效登录
  2. 认证逻辑与context fixture脱节,session级的context未加载到有效认证状态
  3. 默认的page fixture是独立创建的,未关联到已认证的context
  4. 硬编码base_url为8080,与live_server动态分配的端口不匹配,CI环境下会失效

以下是修复后的完整代码:

修复后的conftest.py

import logging
import os
import tempfile

import pytest
from django.conf import settings
from user.models import User
from user.tests.factories import UserFactory

# 适配Django异步环境
os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")
settings.ALLOWED_HOSTS += ["127.0.0.1"]

@pytest.fixture(scope="session")
def user() -> User:
    # 会话级用户,仅创建一次
    return UserFactory()

@pytest.fixture(scope="session")
def playwright_user(user):
    # 初始化测试用户账号,会话级仅执行一次
    password = "testpass123"
    email = "bob@example.com"
    user.email = email
    user.set_password(password)
    user.save()
    logging.info("Created test user: %s", email)
    return {"email": email, "password": password}

@pytest.fixture(scope="session")
def authenticated_context(playwright, playwright_user, live_server):
    # 会话级认证context,仅创建一次,适配CI环境
    # 用临时文件存储认证状态,避免本地文件污染
    with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as tmp:
        storage_path = tmp.name

    # CI环境自动启用无头浏览器
    headless = os.getenv("CI", "false").lower() == "true"
    browser = playwright.chromium.launch(headless=headless)
    
    # 使用live_server动态生成的base_url,避免硬编码端口
    context = browser.new_context(base_url=live_server.url)
    page = context.new_page()

    # 执行登录流程并验证成功
    page.goto("/")
    page.get_by_placeholder("E-mail address login").fill(playwright_user["email"])
    page.get_by_placeholder("Password password").fill(playwright_user["password"])
    page.get_by_role("button", name="Sign In").click()
    
    # 等待登录跳转完成,确保认证状态生效
    page.wait_for_url(f"{live_server.url}/home")
    logging.info("Test user authenticated successfully")

    # 保存认证状态
    context.storage_state(path=storage_path)
    page.close()

    yield context

    # 清理资源
    context.close()
    browser.close()
    os.unlink(storage_path)

@pytest.fixture(scope="function")
def page(authenticated_context):
    # 每个测试用的page都继承已认证的context状态
    page = authenticated_context.new_page()
    yield page
    page.close()

修复后的test_integration.py

import logging

import pytest
from django.urls import reverse

pytestmark = pytest.mark.django_db

def test_login(page):
    # 验证登录状态有效性
    page.goto(reverse("home"))
    assert page.title() == "Home Page"  # 替换为你的首页实际标题
    logging.info("Login state verified")

def test_home(page):
    # 直接访问受保护的首页,无需重复登录
    page.goto(reverse("home"))
    # 断言首页元素存在,确认未被重定向
    assert page.get_by_text("Welcome, bob@example.com").is_visible()  # 替换为你的实际用户标识元素
    logging.info("Home page accessed successfully")

关键改动说明

  • 所有核心逻辑改为会话级(scope="session"),确保认证流程仅执行一次
  • 修复邮箱拼写错误,保证登录操作有效
  • 使用live_server.url动态获取测试服务器地址,适配CI环境的端口分配
  • 用临时文件存储认证状态,避免本地文件残留,兼容CI无持久化环境
  • 自定义page fixture,从已认证的context创建页面,确保所有测试复用登录状态
  • 通过环境变量CI自动切换无头/有头模式,同时支持本地开发调试和CI自动化测试

内容的提问来源于stack exchange,提问作者patoski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:20:22