Introduction
In the realm of web application testing, one of the recurring challenges lies in the need to authenticate or log in for every test iteration. This repetitive process consumes valuable testing time. With each test opening a new browser page, the overhead of authenticating can quickly accumulate, slowing down the testing pipeline.
Fortunately, modern browser automation tools like Playwright offer capabilities that can help alleviate this pain point. By leveraging Playwright’s session storage functionality, it becomes possible to persist authenticated sessions across multiple tests, eliminating the need for redundant login steps.
The solution presented in this article is exemplified in the Pytest fixtures incorporated into my Playwright Python example project, developed in collaboration with Elias Shourosh. These fixtures not only showcase the implementation details but also serve as a practical reference for integrating persistent authentication into your testing workflows, empowering you to maximize your testing productivity.
Implementing the Solution
The solution code can be found here.
@pytest.fixture(scope="function")
def browser_context_args(
browser_context_args: Dict, base_url: str, request: SubRequest
):
"""This fixture allows setting browser context arguments for Playwright.
Args:
browser_context_args (dict): Base browser context arguments.
request (SubRequest): Pytest request object to get the 'browser_context_args' fixture value.
base_url (str): The base URL for the application under test.
Returns:
dict: Updated browser context arguments.
See Also:
https://playwright.dev/python/docs/api/class-browser#browser-new-context
"""
context_args = {
**browser_context_args,
"no_viewport": True,
"user_agent": Constants.AUTOMATION_USER_AGENT,
}
if hasattr(request, "param"):
context_args["storage_state"] = {
"cookies": [
{
"name": "session-username",
"value": request.param,
"url": base_url,
}
]
}
return context_args
This Pytest fixture is designed to set up the arguments for a Playwright browser context. Let’s break down the code:
@pytest.fixture(scope=“function”):
- This decorator defines a function-scoped fixture, meaning it’s executed before each test function. This provides flexibility to tailor context arguments for specific tests.
Parameters:
- browser_context_args (dict): Accepts base browser context arguments, allowing for modification.
- request (SubRequest): Provides access to Pytest-specific features, including retrieving values from other fixtures.
- base_url (str): Holds the base URL of the application under test, used for cookie context setup.
Return Value:
- dict: Returns a dictionary containing the updated browser context arguments, ready to be used in Playwright actions.
Key Actions:
- Maximizing Browser Window: explained in a previous article
- Conditional Cookie Handling:
- Check if the object has an attribute (indicating a test parameterization).
- If present, construct a structure within to set a cookie: Cookie name: Cookie value: (parameter value from the test) Cookie domain: (application’s base URL).
Key takeaways:
- The fixture offers granular control over browser context setup for individual test functions.
- It incorporates conditional logic for cookie management, enabling context customization based on test parameters.
- It demonstrates Playwright’s capabilities for context configuration and test setup with Pytest.
@pytest.fixture(scope="function", autouse=True)
def goto(page: Page, request: SubRequest):
"""Fixture to navigate to the base URL based on the user.
If the 'storage_state' is set in 'browser_context_args', it navigates to the inventory page,
otherwise, it navigates to the login page.
Args:
page (Page): Playwright page object.
request (SubRequest): Pytest request object to get the 'browser_context_args' fixture value.
If 'browser_context_args' is set to a user parameter (e.g., 'standard_user'),
the navigation is determined based on the user.
Example:
@pytest.mark.parametrize('browser_context_args', ["standard_user"], indirect=True)
"""
if request.getfixturevalue("browser_context_args").get("storage_state"):
page.goto("/inventory.html")
else:
page.goto("")
This fixture automates navigation to a specific URL within a test function, adapting based on user context.
@pytest.fixture(scope=“function”, autouse=True):
- This decorator defines a function-scoped fixture () that automatically executes before each test function due to . This ensures navigation happens consistently for every test.
Parameters:
- page (Page): Accepts a Playwright object representing the browser tab or window being used.
- request (SubRequest): Provides access to Pytest functionalities, including retrieving values from other fixtures like .
Navigation Logic:
- Conditional URL Selection: to check if the fixture contains a key. indicates a logged-in user.
- Navigation Based on User Context:
- If exists (logged-in user): Navigates the page to , assuming this is the inventory page for logged-in users.
- Otherwise (no , a new user): Navigate the login page.
Key Takeaways:
- This fixture streamlines navigation by automatically directing users to appropriate URLs based on their login status.
- It demonstrates leveraging for user context awareness during test execution.
Test Usage
The test usage can be found here.
@pytest.mark.parametrize("browser_context_args", ["standard_user"], indirect=True)
def test_inventory_page(self, browser_context_args, page: Page):
assert page.inner_text("//span[@class='title']") == "Products"
Test Decorator:
- : This decorator from pytest applies parameterization to the test. specifies: Parameter name: Values to test: List containing (a user type for browser context setup) indicates that the fixture is used to provide the value, not directly passed as a string.
Test Function Parameters:
- : This parameter is a reference to the test class instance.
- This parameter receives the value from the fixture, which configures browser context based on the provided user type.
- This parameter receives a Playwright object, representing the browser tab or window under test.
In Conclusion
The provided code showcases the implementation of Pytest fixtures in Python Playwright to overcome the challenge of frequent re-login requirements during browser testing. By leveraging Playwright’s session storage capability within the Pytest fixtures, automation engineers can maintain persistent sessions across multiple tests, significantly improving testing efficiency and productivity. The fixtures intelligently manage browser context arguments, ensuring that each test iteration opens within an existing session with predefined cookies, eliminating the need for repetitive login procedures. This approach streamlines the testing workflow and enhances the overall reliability of browser testing. Additionally, a small test usage demonstrates how the fixtures seamlessly integrate into testing scenarios, further highlighting their practicality and effectiveness in real-world testing environments.
Happy testing!