DEV Community

abbazs
abbazs

Posted on

Implementing BDD with `pytest-bdd` and `pytest-playwright` for Web Testing

Introduction

Behavior-Driven Development (BDD) is a software development process that enhances communication between developers, testers, and non-technical stakeholders. It uses simple, natural language syntax to describe the behavior of the application, making it easier for everyone to understand the requirements and the expected behavior.

Example of BDD

Consider a simple feature of logging into a web application. In BDD, this might be described in a feature file using Gherkin syntax as follows:

Feature: User Login

  Scenario: Successful login with valid credentials
    Given the user is on the login page
    When the user enters valid credentials
    Then the user should be redirected to the dashboard
Enter fullscreen mode Exit fullscreen mode

In this example, the behavior of the login feature is clearly described in a way that both technical and non-technical stakeholders can understand.

References

Prerequisites

  • Basic knowledge of Python
  • Familiarity with pytest and web automation concepts
  • Python installed on your machine

Step 1: Setting Up the Environment

Installing Required Packages

First, install the necessary packages:

pip install pytest pytest-bdd pytest-playwright
Enter fullscreen mode Exit fullscreen mode

Step 2: Project Structure

Create a structured project directory as follows:

tutorial/
├── features/
│   ├── login.feature
│   ├── search_stock.feature
│   └── create_screen.feature
├── tests/
│   ├── test_a_login.py
│   ├── test_search_stock.py
│   └── test_create_screen.py
├── conftest.py
└── utils/
    └── config.py
Enter fullscreen mode Exit fullscreen mode

Step 3: Writing Feature Files

Feature files define the behavior you want to test using Gherkin syntax.

features/login.feature

Feature: Login to Screener.in

  Scenario: Log into Screener.in
    Given the user is on the login page
    When the user logs in with valid credentials
    Then the user should be redirected to the dashboard
Enter fullscreen mode Exit fullscreen mode

features/search_stock.feature

Feature: Search for a stock and get the P/E ratio

  Scenario Outline: Search for a stock by name and retrieve the P/E ratio
    Given the user is logged in
    When the user searches for "<stock_name>"
    Then the P/E ratio for "<stock_name>" should be displayed

  Examples:
    | stock_name      |
    | NESTLEIND       |
    | PGHH            |
    | LICI            |
    | TCS             |
    | BRITANNIA       |
Enter fullscreen mode Exit fullscreen mode

features/create_screen.feature

Feature: Create a new screen for quality stocks

  Scenario: Create a new screen with filtering stocks greater than market capital 50000Cr
    Given the user is logged in
    When the user creates a new screen with filtering stocks greater than market capital 50000Cr
    Then the new screen should be created successfully
Enter fullscreen mode Exit fullscreen mode

Step 4: Writing Step Definitions

Step definitions map the steps in your feature files to Python functions.

tests/test_a_login.py

import pytest
from pytest_bdd import scenarios, given, when, then

scenarios('../features/login.feature')

@given('the user is on the login page')
def navigate_to_login_page(page):
    page.goto("https://www.screener.in/login/")

@when('the user logs in with valid credentials')
def login_with_valid_credentials(login):
    pass  # The login fixture handles the login

@then('the user should be redirected to the dashboard')
def verify_dashboard(login):
    assert login.url == "https://www.screener.in/dash/"
Enter fullscreen mode Exit fullscreen mode

tests/test_search_stock.py

import pytest
from pytest_bdd import scenarios, given, when, then, parsers
from playwright.sync_api import Page
import time

scenarios('../features/search_stock.feature')

@given('the user is logged in')
def user_logged_in(login):
    pass  # The login fixture ensures the user is logged in

@when(parsers.parse('the user searches for "{stock_name}"'))
def search_stock(login: Page, stock_name):
    login.click('//*[@id="desktop-search"]/div/input')
    login.fill('//*[@id="desktop-search"]/div/input', stock_name)
    login.click('//*[@id="desktop-search"]/div/div/button')

@then(parsers.parse('the P/E ratio for "{stock_name}" should be displayed'))
def verify_pe_ratio(login: Page, stock_name):
    pe_ratio = login.locator('//*[@id="top-ratios"]/li[4]/span[2]')
    assert pe_ratio.is_visible()
    print(f"P/E Ratio for {stock_name}: {pe_ratio.text_content()}")
    time.sleep(5)
Enter fullscreen mode Exit fullscreen mode

tests/test_create_screen.py

import pytest
from pytest_bdd import scenarios, given, when, then
from playwright.sync_api import Page

scenarios("../features/create_screen.feature")

@given("the user is logged in")
def user_logged_in(login):
    pass  # The login fixture ensures the user is logged in

@when("the user creates a new screen with filtering stocks greater than market capital 50000Cr")
def create_new_screen(login: Page):
    login.click("//a[@href='/explore/' and text()='Screens']")
    login.click("//a[@class='button button-primary' and @href='/screen/new/']")
    login.fill(
        'textarea[name="query"]',
        """Market Capitalization > 50000""",
    )
    login.click("//button[@class='button-primary']")

@then("the new screen should be created successfully")
def verify_new_screen_creation(login: Page):
    assert login.locator("text=Nestle India").is_visible()
Enter fullscreen mode Exit fullscreen mode

Step 5: Configuring pytest-playwright

Set up Playwright in your conftest.py to handle browser instances.

conftest.py

import pytest
from playwright.sync_api import sync_playwright
from utils.config import USERNAME, PASSWORD

@pytest.fixture(scope="session")
def browser():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        yield browser
        browser.close()

@pytest.fixture(scope="session")
def context(browser):
    context = browser.new_context()
    yield context
    context.close()

@pytest.fixture(scope="session")
def page(context):
    page = context.new_page()
    yield page
    page.close()

@pytest.fixture(scope="session")
def login(page):
    page.goto("https://www.screener.in/login/")
    page.fill('input[name="username"]', USERNAME)
    page.fill('input[name="password"]', PASSWORD)
    page.click('button[type="submit"]')
    assert page.url == "https://www.screener.in/dash/"
    yield page
Enter fullscreen mode Exit fullscreen mode

utils/config.py

Ensure you have your credentials stored securely:

USERNAME = "your_username"
PASSWORD = "your_password"
Enter fullscreen mode Exit fullscreen mode

Step 6: Running the Tests

To run your tests, simply use the pytest command:

pytest
Enter fullscreen mode Exit fullscreen mode

Conclusion

This tutorial provided a detailed introduction to setting up and using pytest-bdd and pytest-playwright for BDD testing. By following the steps above, you can create robust and readable tests for your web applications.

Feel free to extend this setup to include more complex scenarios and additional utilities as needed. This setup provides a solid foundation for using pytest-bdd and pytest-playwright in your projects.

References


By following this guide, you'll be well on your way to implementing effective BDD-style tests for your web applications. Happy testing!

Top comments (1)

Collapse
 
stanciuvalenti8 profile image
Stanciu Valentin

amazing tutorial, thank you. exactly what I was looking for. Simple and it just works. Thank you!