Search code examples
pythonpytestplaywrightevent-loop

How to make an event_loop the running loop in Python without calling run


I use the Playwright (sync) library to automate UI testing with Python. I wrote some test cases and everything worked fine like this. start_page is a fixture that handles opening the browser, logging in and navigating to the start page.

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

But when changing a test case to async like this:

@pytest.mark.asyncio
async def test_my_test_case(start_page: Page):
    page.goto(URL)

I started getting this error:

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'>

In the Playwright library I found this code:

try:
    self._loop = asyncio.get_running_loop()
except RuntimeError:
    self._loop = asyncio.new_event_loop()
    self._own_loop = True

While I have a fixture in my tests that creates an own event_loop:

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

I figured out that I have to make loop the running loop so that Playwright uses the same loop as my tests. When I add asyncio._set_running_loop(loop) like this:

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

Everything works fine, but this does not seem like a good practice.I can not call run as there is no function to call it on.

Any solutions?


Solution

  • I found a solution, I just had to switch to the async_api of playwright.

    import asyncio
    import nest_asyncio
    import pytest
    import pytest_asyncio
    
    from playwright.async_api import async_playwright, Page
    
    
    @pytest.fixture(scope="module")
    def event_loop():
        nest_asyncio.apply()
        loop = asyncio.new_event_loop()
        yield loop
        loop.close()
    
    
    @pytest_asyncio.fixture(scope="module")
    @pytest.mark.asyncio
    async def my_page() -> None:
        async with async_playwright() as pw:
            browser = await pw.chromium.launch(headless=False, slow_mo=500)
            page = await browser.new_page()
            yield page
            await browser.close()
    
    
    @pytest.mark.asyncio
    async def test_my_test_case(my_page: Page):
        await my_page.goto(URL)
    

    I will update my existing tests and hope that everything works.