如何在pytest-playwright与Django中复用测试登录状态?
问题分析与解决方案
你的代码存在几个核心问题导致登录状态无法复用:
logged_infixture中邮箱拼写错误(bob@exmpale.com少了字母l),实际未完成有效登录- 认证逻辑与
contextfixture脱节,session级的context未加载到有效认证状态 - 默认的
pagefixture是独立创建的,未关联到已认证的context - 硬编码
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无持久化环境
- 自定义
pagefixture,从已认证的context创建页面,确保所有测试复用登录状态 - 通过环境变量
CI自动切换无头/有头模式,同时支持本地开发调试和CI自动化测试
内容的提问来源于stack exchange,提问作者patoski
相关产品推荐
相关产品推荐

