无法修复Playwright报错:ValueError与TargetClosedError问题求助
解决Playwright异步API结合Pytest的事件循环不匹配与TargetClosedError问题
报错信息
ValueError: The future belongs to a different loop than the one specified as the loop argument.
Future exception was never retrieved
future: <Future finished exception=TargetClosedError('Target page, context or browser has been closed')>
playwright._impl._errors.TargetClosedError: Target page, context or browser has been closed
原代码片段
conftest.py
import pytest import pyotp from playwright.async_api import async_playwright browsers = ["chromium"] @pytest.fixture(scope="session", params=browsers) async def browser_context_creation_admin(request): browser_type = request.param async with async_playwright() as p: browser = await getattr(p, browser_type).launch(headless=False, slow_mo=300) context = await browser.new_context() page = await context.new_page() page.set_default_timeout(20000) await page.goto("") await page.get_by_label("Username").click() await page.get_by_label("Username").fill("") await page.get_by_role("button", name="Next").click() await page.get_by_role("link", name="Select").first.click() totp = pyotp.parse_uri() generated_otp = totp.now() await page.get_by_label("Enter code").fill(generated_otp) await page.get_by_role("button", name="Verify").click() await page.get_by_role("link", name="Select").nth(1).click() await page.get_by_label("Password").fill("") await page.get_by_role("button", name="Verify").click() await page.get_by_role("button", name="I Accept").click() yield context await context.close() await browser.close() @pytest.fixture() async def login_set_up_admin(browser_context_creation_admin): admin_context = browser_context_creation_admin admin_page = await admin_context.new_page() await admin_page.goto("") admin_context.set_default_timeout(20000) yield admin_page await admin_page.close()
test_login.py
async def test_login_with_valid_creds_admin(login_set_up_admin): admin_page = login_set_up_admin await admin_page.locator('xpath=//button[@id="icon-btn-3"]//div[@class="notification"]//*[name()="svg"]//' '*[name()="path" and contains(@d,"M16,2A14,1")]').click() assert await admin_page.get_by_role("button", name="Logout").is_visible()
错误原因分析
- 事件循环不匹配:
async_playwright()的上下文管理器(async with)会在代码块结束时自动销毁Playwright实例,但你把yield context放在了这个上下文块内部。这导致yield后Playwright实例被强制关闭,后续测试使用的context属于已销毁的实例,从而引发事件循环不匹配错误。 - TargetClosedError:由于Playwright实例被提前销毁,关联的浏览器、上下文资源也被强制关闭,后续对页面的操作自然会触发"目标已关闭"的错误。
修复后的代码
conftest.py
import pytest import pyotp from playwright.async_api import async_playwright browsers = ["chromium"] @pytest.fixture(scope="session", params=browsers) async def browser_context_creation_admin(request): browser_type = request.param # 手动初始化Playwright,不使用async with,保持实例生命周期与session一致 p = await async_playwright().start() browser = await getattr(p, browser_type).launch(headless=False, slow_mo=300) context = await browser.new_context() page = await context.new_page() page.set_default_timeout(20000) # 替换为实际的登录URL和凭证 await page.goto("你的实际登录URL") await page.get_by_label("Username").click() await page.get_by_label("Username").fill("你的用户名") await page.get_by_role("button", name="Next").click() await page.get_by_role("link", name="Select").first.click() totp = pyotp.parse_uri("你的TOTP URI") generated_otp = totp.now() await page.get_by_label("Enter code").fill(generated_otp) await page.get_by_role("button", name="Verify").click() await page.get_by_role("link", name="Select").nth(1).click() await page.get_by_label("Password").fill("你的密码") await page.get_by_role("button", name="Verify").click() await page.get_by_role("button", name="I Accept").click() yield context # 按顺序清理资源:先关context,再关browser,最后停止Playwright await context.close() await browser.close() await p.stop() @pytest.fixture() async def login_set_up_admin(browser_context_creation_admin): admin_context = browser_context_creation_admin admin_page = await admin_context.new_page() # 替换为实际的应用首页URL await admin_page.goto("你的应用首页URL") admin_context.set_default_timeout(20000) yield admin_page await admin_page.close()
额外注意事项
- 务必将代码中所有占位的URL、用户名、密码、TOTP URI替换为实际有效值,否则登录流程会直接失败。
- 当前
login_set_up_adminfixture已经在测试完成后自动关闭页面,无需额外修改。 - session级fixture的设计可以避免每个测试重复执行登录流程,提升测试效率,这个思路是合理的。
内容的提问来源于stack exchange,提问作者Basavaraj Lamani
相关产品推荐
相关产品推荐

