Creating Asynchronous Automation test using Playwright & pytest

Qxf2 explored an advanced capability with Playwright recently – writing asynchronous tests. Given our admiration for pytest we picked the Playwright-pytest plugin for our research. While we were impressed with the pytest integration, we noticed the official and community documentation only featured synchronous examples using pytest. This post will hopefully bridge this gap. We figured working on fitting pytest with asynchronous Playwright will also help us understand Playwright internals better. In this post we will show you how to go about creating a simple Playwright UI test using pytest.
Note: This post assumes that: you are already familiar with Playwright and have used pytest to write synchronous UI tests.


Creating asynchronous pytest fixture

pytest’s preloaded fixtures for Playwright makes writing tests a very straightforward task but unfortunately the pytest fixtures only uses the synchronous APIs. The challenge is to create a coroutine fixture. We had used pytest_asyncio before to create asynchronous API test, we will be using pytest_asyncio to create a asynchronous fixture to:
– Start Playwright
– Launch a browser
– Create a new page

# Contents of conftest.py file
import pytest_asyncio
from playwright.async_api import async_playwright
 
@pytest_asyncio.fixture
async def async_page(pytestconfig):
    "Async fixture to return a Playwright Page"
    browser = pytestconfig.getoption("--browser")
    async with async_playwright() as playwright:
        browser_obj = getattr(playwright,browser[0])
        if browser_obj:
            browser = await browser_obj.launch()
            page = await browser.new_page()
            yield page

The asynchorous fixture uses a context manager to start the Playwright service and stop it at exit, it supports a --browser CLI parameter to get the browser input and returns a new page

Disclaimer: The code in this blog post is intended for demonstration purposes only. We recommend following better coding standards for real-world applications.


Creating an Asynchronous UI Automation test

Now that we have an asynchronous fixture, creating a test is an easy task. We can create coroutine test functions using pytest_asyncio. The tests we create will validate the following scenarios:
– Landing on a page
– Form in the page

Validate landing on a page

The test scenario for the first test function is to validate landing on Selenium Tutorial Page. We can use the async_page fixture we created to create the test function. The @pytest.mark.asyncio marker needs to be used for pytest to collect the coroutine as a test.

@pytest.mark.asyncio
async def test_has_title(async_page):
    "Validate Tutorial Main page title"
    await async_page.goto("https://qxf2.com/selenium-tutorial-main")
    title = await async_page.title()
    assert title == "Qxf2 Services: Selenium training main"
Validate the form in the page

The test scenario for the second test function is to validate the example form in the same page

@pytest.mark.asyncio
async def test_contact_form(async_page):
    "Validate contact form"
    await async_page.goto("https://qxf2.com/selenium-tutorial-main")
    await async_page.get_by_role("textbox", name="name").fill("Jonathan Wick")
    await async_page.locator("xpath=//input[@name='email']").fill("[email protected]")
    await async_page.locator("xpath=//input[@name='phone']").fill("0000000000")
    await async_page.locator("xpath=//button[@type='submit']").click()
    redirect_title = await async_page.title()
    assert redirect_title == "Qxf2 Services: Selenium training redirect"
Putting it all together

Consolidating the test functions in a test_async_tutorial_page.py module:

@pytest.mark.asyncio
async def test_has_title(async_page):
    "Validate Tutorial Main page title"
    await async_page.goto("https://qxf2.com/selenium-tutorial-main")
    title = await async_page.title()
    assert title == "Qxf2 Services: Selenium training main"
 
@pytest.mark.asyncio
async def test_contact_form(async_page):
    "Validate contact form"
    await async_page.goto("https://qxf2.com/selenium-tutorial-main")
    await async_page.get_by_role("textbox", name="name").fill("Jonathan Wick")
    await async_page.locator("xpath=//input[@name='email']").fill("[email protected]")
    await async_page.locator("xpath=//input[@name='phone']").fill("0000000000")
    await async_page.locator("xpath=//button[@type='submit']").click()
    redirect_title = await async_page.title()
    assert redirect_title == "Qxf2 Services: Selenium training redirect"

Note: In the real world, this example would be very useful if you are automating a survey with dozens of questions.

Running the test

To run the test use the following command:

pytest --browser chromium -s -v

Async Playwright pytest run


There you have it, a simple asynchronous Playwright UI test that uses pytest. Like we mentioned at the start of this post only features a basic example on how to use pytest to create an asynchronous Playwright UI test but if you wish to create/modify your own offshoot of Playwright pytest framework and wish to make it support async refer Playwright-Python’s own async conftest.py file to create your async fixtures.


Hire testers from Qxf2

To stay competitive and relevant Qxf2 services make every effort to adapt to the latest technological trends. We constantly explore new tools and frameworks to improve our technical footprint and to identify and implement useful features to our open-source framework on GitHub here – Qxf2 Page Object Model Framework. If you are looking for technical testers to help create automation tests for your ever evolving product contact Qxf2.


Leave a Reply

Your email address will not be published. Required fields are marked *