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

无法修复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()

错误原因分析

  1. 事件循环不匹配:async_playwright()的上下文管理器(async with)会在代码块结束时自动销毁Playwright实例,但你把yield context放在了这个上下文块内部。这导致yield后Playwright实例被强制关闭,后续测试使用的context属于已销毁的实例,从而引发事件循环不匹配错误。
  2. 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_admin fixture已经在测试完成后自动关闭页面,无需额外修改。
  • session级fixture的设计可以避免每个测试重复执行登录流程,提升测试效率,这个思路是合理的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:27:10