How can I share a Page object among multiple asynchronous Playwright tests in Python to speed up execution?

When running async Playwright tests in Python using async_playwright(), creating a new browser and page for each test makes even basic tests slow (~50s for 3 tests).

Using a single Page object in sync tests is faster, but replicating this in async tests is tricky.

Original async test code:

from playwright.async_api import async_playwright

def test_has_title():
    with async_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context()
        page = context.new_page()

        # Go to the Playwright documentation page
        page.goto("https://playwright.dev/")
        print(page.title())

Original sync test code (faster):

from playwright.sync_api import Page

def test_has_title(page: Page):
    page.goto("https://playwright.dev/")
    print(page.title())

Attempted async approach using a shared Page object (didn’t work):

import pytest
from playwright.async_api import Page

@pytest.mark.asyncio
async def test_has_title(page: Page):
    await page.goto("https://playwright.dev/")
    print(await page.title())

Package versions:

  • playwright 1.51.0
  • pytest 8.3.5
  • pytest-asyncio 0.26.0
  • pytest-base-url 2.1.0
  • pytest-dotenv 0.5.2
  • pytest-playwright 0.7.0

I ran into the same issue when moving from sync to async tests. The key is to leverage the page fixture from pytest-playwright, which handles the browser and context setup for you.

You can use it in async tests like this:

import pytest

@pytest.mark.asyncio
async def test_has_title(page):
    await page.goto("https://playwright.dev/")
    title = await page.title()
    print(title)

pytest-playwright ensures that a single browser context is shared between tests if you configure the scope="session" in your own fixture, so tests run much faster than launching a browser each time.

In my async tests, I created a session-scoped fixture that launches the browser once and returns a Page object to all tests:

import pytest
from playwright.async_api import async_playwright

@pytest.fixture(scope="session")
async def page():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()
        yield page
        await browser.close()

@pytest.mark.asyncio
async def test_title(page):
    await page.goto("https://playwright.dev/")
    print(await page.title())

@pytest.mark.asyncio
async def test_url(page):
    await page.goto("https://playwright.dev/docs/intro")
    print(await page.url())

This way, the same browser and page are reused, dramatically speeding up test execution.

I like to share the browser context instead of the exact Page for async tests. That way, each test still gets a clean page but you avoid launching the browser multiple times:

@pytest.fixture(scope="session")
async def context():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        context = await browser.new_context()
        yield context
        await browser.close()

@pytest.mark.asyncio
async def test_one(context):
    page = await context.new_page()
    await page.goto("https://playwright.dev/")
    print(await page.title())
    await page.close()

@pytest.mark.asyncio
async def test_two(context):
    page = await context.new_page()
    await page.goto("https://playwright.dev/docs/api/class-playwright")
    print(await page.title())
    await page.close()

This gives a good balance between speed and test isolation, because each test still gets a fresh page without repeatedly starting the browser.