1

I'm using async_playwright in Python. When I want to add unit tests, I need to create a shared browser and page for each test case. I'm using pytest_asyncio to create fixtures for that.

This is my conftest.py file:

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

And this is my test file:

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")

When I execute pytest tests (where tests is the folder that contains the previous files), the first test case passes and then the execution gets stuck forever.

What am I missing here?

Things I have tried:

  • I had the module scoped fixtures in the test file before and the behavior was the same. I understand that it doesn't change anything.
  • I removed the event_loop fixture from the conftest.py file and got the same result.
  • I have been looking for ways to run all my tests without using the Playwright plugin for pytest since I think there should be a way to run my tests without it.
  • I found a similar question here, but it has no answer so I'm asking about my specific case that also involves playwright.

Update (2024-04-25)

This is the output of pytest -vvv -s:

(example) C:\Users\MyUser\Desktop\projects\example>pytest -vvv -s
================================================= test session starts =================================================
platform win32 -- Python 3.12.2, pytest-8.1.1, pluggy-1.4.0 -- C:\Users\MyUser\miniconda3\envs\example\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\MyUser\Desktop\projects\example
plugins: asyncio-0.23.6
asyncio: mode=Mode.STRICT
collected 5 items

tests/test_pageobject.py::test_po_factory https://www.google.com
<src.page_object.page.playwright.PWPageObject object at 0x0000018A4EA38FB0>
PASSED
tests/test_pageobject.py::test_get_active_element

The process stays stuck forever :(

Maybe other details I didn't mention:

  • I'm developing inside a conda environment
  • I'm using a Windows machine
3
  • Can you post output of pytest -vvv -s? Commented Apr 24, 2024 at 8:05
  • @Shod I added the requested info. Thanks a lot for reviewing my issue! Commented Apr 25, 2024 at 12:40
  • i can suggest you 2 things - try to replace return with yield in the page fixture, and secondly try to add a pdb in test_get_active_element, run pytest and check line-by-line.. i suspect some issue in the get_active_element method Commented Apr 29, 2024 at 16:00

1 Answer 1

1

The solution is - add a loop_scope param to the asyncio marker in the test and set it to module.

Like this:

@pytest.mark.asyncio(loop_scope="module")
async def test_po_factory(page):
    assert page is not None
    assert isinstance(page, PWPageObject)

Add this parameter to all the tests in the module.

And you can remove the event_loop fixture.

Reference - asyncio event loops

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.