使用Pytest+async playwright测试:首个测试通过后进程永久挂起
问题:async_playwright + pytest_asyncio 模块级fixture导致测试挂起
我使用async_playwright编写UI测试,通过pytest_asyncio创建module级别的browser和page共享fixture以复用实例,但执行pytest tests时,首个测试用例通过后进程永久挂起。
代码实现
conftest.py
import pytest_asyncio import asyncio from playwright.async_api import async_playwright from src.page_object.factory.playwright import PWPageOjectFactory @pytest_asyncio.fixture(scope="session") def event_loop(request): """Create an instance of the default event loop for each test case.""" loop = asyncio.get_event_loop_policy().new_event_loop() yield loop loop.close() @pytest_asyncio.fixture(scope="module") async def browser(): async with async_playwright() as p: browser = await p.chromium.launch() yield browser await browser.close() @pytest_asyncio.fixture(scope="module") async def page(browser): factory = PWPageOjectFactory("https://www.google.com", browser) print(factory.url) page = await factory.create_page_object() print(page) return page
测试文件代码
import pytest from src.page_object.element.playwright import PWElement from src.page_object.page.playwright import PWPageObject pytest_plugins = ("pytest_asyncio",) @pytest.mark.asyncio async def test_po_factory(page): assert page is not None assert isinstance(page, PWPageObject) @pytest.mark.asyncio async def test_get_active_element(page): element = await page.get_active_element() assert element is not None assert isinstance(element, PWElement) @pytest.mark.asyncio async def test_navigate(page): element = await page.get_active_element() await page.navigate_forward() new_element = await page.get_active_element() assert element.get_attribute("id") != new_element.get_attribute("id") await page.navigate_backward() new_element = await page.get_active_element() assert element.get_attribute("id") == new_element.get_attribute("id")
已尝试操作
- 将module级fixture移至测试文件,问题依旧;
- 删除conftest.py中的event_loop fixture,结果无变化;
- 尝试不使用Playwright的pytest插件运行测试;
- 查找类似问题但未找到有效解答。
环境信息
- 系统:Windows
- 环境:conda
解决方案
1. 修正Playwright启动/关闭方式
原browser fixture使用async with async_playwright(),在module级fixture场景下可能导致上下文提前回收,改为手动启动和停止Playwright:
@pytest_asyncio.fixture(scope="module") async def browser(): p = await async_playwright().start() browser = await p.chromium.launch() yield browser await browser.close() await p.stop()
2. 调整Windows下的Event Loop策略
Windows默认的ProactorEventLoop对部分异步操作支持不佳,切换为SelectorEventLoopPolicy:
在conftest.py开头添加:
import sys import asyncio from asyncio import WindowsSelectorEventLoopPolicy, WindowsProactorEventLoopPolicy if sys.platform.startswith('win') and isinstance(asyncio.get_event_loop_policy(), WindowsProactorEventLoopPolicy): asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
3. 检查Page对象的状态管理
如果PWPageOjectFactory.create_page_object()复用了同一个浏览器上下文或页面,测试间的状态残留可能导致挂起:
- 确保每个测试后重置页面状态(如
await page.goto("https://www.google.com")或await page.reload()); - 若无需强复用,可将page fixture的scope改为
function验证问题:
@pytest_asyncio.fixture(scope="function") async def page(browser): factory = PWPageOjectFactory("https://www.google.com", browser) page = await factory.create_page_object() yield page await page.close()
4. 排查测试用例中的异步操作
test_navigate中的navigate_forward可能因页面无历史记录导致阻塞,添加前置导航生成历史:
@pytest.mark.asyncio async def test_navigate(page): # 先导航到其他页面生成历史记录 await page.goto("https://www.github.com") await page.goto("https://www.google.com") element = await page.get_active_element() await page.navigate_forward() new_element = await page.get_active_element() assert element.get_attribute("id") != new_element.get_attribute("id") await page.navigate_backward() new_element = await page.get_active_element() assert element.get_attribute("id") == new_element.get_attribute("id")
内容的提问来源于stack exchange,提问作者Jose Jorge Rodriguez Salgado
相关产品推荐
相关产品推荐

