如何在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'>
原因分析
- Playwright同步API的内部逻辑:如果检测不到当前运行的event_loop,会自动新建一个独立循环
try: self._loop = asyncio.get_running_loop() except RuntimeError: self._loop = asyncio.new_event_loop() self._own_loop = True - 测试中自定义的
event_loopfixture仅创建了循环但未将其设为当前运行的循环,导致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
相关产品推荐
相关产品推荐

