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

如何在Python中无需调用run使event_loop成为运行循环?

解决Playwright异步测试与自定义event_loop的循环冲突问题

问题场景

原本基于Playwright同步API的UI自动化测试运行正常:

def test_my_test_case(start_page: Page):
    page.goto(URL)

但将测试改为异步形式后,出现循环不匹配的RuntimeError:

Failed: [undefined]Failed: Unexpected err=RuntimeError("Task <Task pending name='Task-12' coro=<Page.goto() running at ...\\_page.py:495> cb=[SyncBase._sync.<locals>.<lambda>() at ...\\_sync_base.py:100]> got Future <Future pending> attached to a different loop"), type(err)=<class 'RuntimeError'>

原因分析

  1. Playwright同步API的内部逻辑:如果检测不到当前运行的event_loop,会自动新建一个独立循环
    try:
        self._loop = asyncio.get_running_loop()
    except RuntimeError:
        self._loop = asyncio.new_event_loop()
        self._own_loop = True
    
  2. 测试中自定义的event_loop fixture仅创建了循环但未将其设为当前运行的循环,导致Playwright同步API新建的循环与测试用循环不一致,触发跨循环任务绑定错误。

合规解决方案

核心思路:统一使用Playwright异步API,让测试与Playwright共享同一个event_loop,避免跨循环冲突。

方案1:切换为Playwright异步API编写测试

1. 重构异步版start_page fixture

import pytest
from playwright.async_api import async_playwright, Page

@pytest.fixture(scope="module")
async def start_page():
    async with async_playwright() as p:
        # 启动浏览器
        browser = await p.chromium.launch(headless=False)
        page = await browser.new_page()
        
        # 执行登录及起始页导航逻辑
        await page.goto("your_login_url")
        await page.fill("#username", "test_user")
        await page.fill("#password", "test_pass")
        await page.click("#login-btn")
        await page.goto("your_start_url")
        
        yield page
        
        # 清理资源
        await browser.close()

2. 编写异步测试用例

import pytest

@pytest.mark.asyncio
async def test_my_test_case(start_page: Page):
    await start_page.goto(URL)  # 调用异步方法,需加await
    # 后续异步操作示例
    # await start_page.click("#target-btn")
    # assert await start_page.locator("#result").text_content() == "expected"

方案2:正确配置自定义event_loop(保留同步API场景)

如果因特殊需求必须使用Playwright同步API,需确保自定义循环成为当前运行的循环,且使用合规API实现:

import pytest
import asyncio
import nest_asyncio

@pytest.fixture(scope="module")
def event_loop():
    nest_asyncio.apply()
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()

同时,确保测试用例中的Playwright同步操作在该循环上下文执行,避免跨循环调用。

关键注意事项

  • 禁止使用asyncio._set_running_loop()这类私有API:属于Python内部实现细节,版本兼容性无保障,违反最佳实践。
  • 异步测试必须搭配Playwright异步API:同步API内部会创建独立循环,必然与测试循环冲突。
  • 依赖pytest-asyncio管理循环:默认情况下pytest-asyncio会自动创建并维护测试循环,无需自定义,仅在特殊场景(如嵌套异步)才需自定义fixture。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:12:51