Skip to content

Latest commit

 

History

History
303 lines (248 loc) · 12.5 KB

File metadata and controls

303 lines (248 loc) · 12.5 KB

Selenium + Pytest + Allure Test Framework (Page Object Model)

A cross-browser (Chrome / Firefox / Edge) UI test framework built with Selenium 4, pytest, and Allure reporting, following the Page Object Model (POM). Deliberately kept to a single worked example - reading a web page's title - so the whole framework can be read end to end.

Features

  • Page Object Model: an object repository (pages/) keeps every locator and UI interaction method separate from the test scripts. Test files never call driver.find_element or reference a By locator directly - they only call methods on a page object.
  • Cross-browser: run against Chrome, Firefox, or Edge via a CLI flag, using manually installed driver binaries (see drivers/).
  • Screenshots on failure: automatically captured and attached to the Allure report, along with the failing page's URL and HTML source.
  • Allure reporting, generated automatically: every test run produces a fresh Allure HTML report with no extra command needed (see below).
  • Logging: every run writes a timestamped log file to logs/.
  • Data-driven ready: utils/data_reader.py reads test data from CSV (csv module), Excel (openpyxl), JSON (json module), and YAML (PyYAML) - no pandas dependency - currently unused by the single example, ready for when a data-driven test is added.
  • Single source of truth for the URL: the target URL (APP_URL) lives only in conftest.py. The driver fixture navigates there automatically before every test, so test files never hardcode or reference it.
  • CI/CD ready: a Jenkinsfile runs the same tests in Jenkins (see "Jenkins CI/CD Integration" below).
  • Email notifications: an opt-in summary email (total/passed/failed and percentages), sent by the exact same code whether triggered locally or from Jenkins (see "Email Notifications" below).

Project Structure

selenium_test_framework/
├── conftest.py               # fixtures, CLI options, screenshot & Allure-report hooks
├── pytest.ini
├── requirements.txt
├── Jenkinsfile                # Jenkins declarative pipeline (see "Jenkins CI/CD" below)
├── base/
│   └── base_driver.py        # BaseDriver - common driver handle + logger shared by all page objects
├── pages/
│   └── title_page.py         # TitlePage(BaseDriver) - the framework's single page object
├── utils/
│   ├── logger.py             # shared logging setup
│   ├── data_reader.py        # CSV / Excel / JSON / YAML test data readers (no pandas)
│   └── email_report.py       # total/passed/failed summary email (see "Email Notifications" below)
├── test_data/
│   └── README.md             # empty for now - see "Adding Test Data" below
├── tests/
│   └── test_title.py         # the framework's single test - uses the `title_page` fixture only
├── drivers/                  # manually installed driver binaries go here
│   └── README.md
├── logs/                     # generated per run
├── screenshots/              # generated on failure
└── reports/
    ├── allure-results/       # raw results (generated every run)
    └── allure-report/        # HTML report (generated every run, automatically)

Page Object Model

  • base/base_driver.pyBaseDriver holds the shared self.driver handle and a self.logger. Every page object inherits from it.
  • pages/ — one class per page of the application. Each class holds that page's locators (as class attributes) and the methods used to interact with it. pages/title_page.py is the framework's only page object right now, exposing get_title().
  • tests/ — test scripts depend on a page-object fixture (title_page, defined in conftest.py) and only call its methods. If the UI changes, only the page object needs updating - the tests stay untouched.

To add a new page: create pages/<new_page>.py with a class inheriting BaseDriver, add its locators and methods - that's it, no changes to conftest.py needed. Use it in a test via the generic page factory fixture:

from pages.login_page import LoginPage

def test_login(page):
    login = page(LoginPage)
    login.enter_username("...")

page(SomeClass) builds the page object with the current driver, calls its wait_for_page_to_load() if it defines one, and caches it per test. This is what lets the framework scale to many pages without conftest.py accumulating one near-identical fixture per page. title_page still exists as a named convenience fixture (it's just page(TitlePage) under the hood) purely for readability in the existing test - it's optional, not a pattern you need to repeat for every new page.

Setup

  1. Create and activate a virtual environment (recommended):

    python3 -m venv venv
    source venv/bin/activate      # Windows: venv\Scripts\activate
  2. Install dependencies:

    pip install -r requirements.txt
  3. Install the Allure command-line tool. This is required both to view the report and for the framework's auto-generation hook to build it (result collection itself is handled by allure-pytest):

  4. Driver binaries are manual — this framework does not auto-download them. Download the driver matching your installed browser version and place it in drivers/ using the exact filename below (see drivers/README.md for links):

    Browser File to place in drivers/
    Chrome chromedriver.exe
    Firefox geckodriver.exe
    Edge msedgedriver.exe

    If a browser isn't installed in its default Windows location, set its path in BROWSER_BINARY_PATHS at the top of conftest.py.

    If a required driver binary is missing, the framework fails fast with a clear error naming the exact file it expected and where.

Running Tests

pytest.ini already passes --alluredir=reports/allure-results by default, so you don't need to add that flag yourself.

Run everything against Chrome (default):

pytest

Run against a specific browser:

pytest --browser=firefox
pytest --browser=edge

Run headless (useful in CI):

pytest --browser=chrome --headless

Run only smoke tests:

pytest -m smoke

Jenkins CI/CD Integration

Tested against Jenkins 2.375.3 with the Pipeline, Git, and Allure Jenkins plugins installed, running a Windows agent (matching the rest of this framework's Windows-specific setup - drivers/ binaries, bat steps).

1. Prerequisites on the Jenkins agent

  • Jenkins 2.375.3 (or compatible) up and running.
  • Python (matching the version this framework was built against) on the agent's PATH.
  • Chrome, Firefox, and/or Edge installed on the agent, with the matching driver binaries already placed in drivers/ inside the job's workspace (or committed into the repository the job checks out).
  • The Allure commandline tool installed on the agent, or installable via Jenkins' own tool auto-installer (see step 2).

2. Install the required Jenkins plugins

  1. Manage Jenkins → Plugins → Available plugins.
  2. Search for and install: Allure Jenkins Plugin (Pipeline and Git plugins are bundled with Jenkins by default in most installations; install them too if missing).
  3. Restart Jenkins if prompted.
  4. Manage Jenkins → Tools → Allure Commandline installations → Add Allure Commandline. Give it a name (e.g. allure) and either point it at an existing installation or tick "Install automatically".

3. Add the project to Jenkins

  1. New Item → enter a job name → select PipelineOK.
  2. Under Pipeline, set Definition to Pipeline script from SCM.
  3. Set SCM to Git, enter the repository URL and credentials, and set the branch to build.
  4. Leave Script Path as Jenkinsfile (the default) - the pipeline definition already sits at the project root.
  5. Save.

4. Run the build

  • Click Build with Parameters (this appears after the first run, once Jenkins has read the parameters block in the Jenkinsfile - the very first build may need to be triggered once with Build Now before the parameterised form appears).
  • Choose BROWSER (chrome / firefox / edge) and whether to run HEADLESS, then Build.

5. View the results

  • The build page shows an Allure Report link (added by the Allure Jenkins plugin), including trend graphs across builds.
  • Console Output shows the same pytest terminal output you would see running locally.
  • Screenshots and logs for the run are available under the build's Build Artifacts, archived by the post { always { ... } } block in the Jenkinsfile.

Note that the framework's own pytest_sessionfinish hook (see conftest.py) still tries to build reports/allure-report/ locally on the agent after every run, exactly as it does outside Jenkins - this is harmless and independent of the Jenkins-hosted Allure report the plugin publishes.

Email Notifications

utils/email_report.py can send a summary email (total tests, passed, failed, and the passed/failed percentages) after a run finishes, to fullstack_tester@zohomail.in. The same code runs identically whether pytest is invoked locally or from Jenkins - Jenkins just runs the same pytest command with the same flag.

It is opt-in via --send-email, and reads SMTP credentials from two environment variables rather than anything hardcoded:

  • EMAIL_SENDER - the Zoho Mail address to send from.
  • EMAIL_PASSWORD - that account's SMTP (app-specific) password.

Running locally:

set EMAIL_SENDER=your_address@zohomail.in
set EMAIL_PASSWORD=your_smtp_app_password
pytest --send-email

(PowerShell: use $env:EMAIL_SENDER = "..." instead of set.)

If either variable is missing, the run still completes normally - the hook logs a warning and skips sending, rather than failing the test run.

Running via Jenkins: the Jenkinsfile already passes --send-email and supplies EMAIL_SENDER / EMAIL_PASSWORD from a Jenkins credential named zoho-email-credentials (a "Username and password" credential: username = sender address, password = SMTP app password). Create that credential under Manage Jenkins → Credentials before running the pipeline; no other setup or code change is needed.

Allure Report — generated automatically every run

At the end of every pytest run, conftest.py's pytest_sessionfinish hook shells out to the Allure commandline tool and rebuilds reports/allure-report/ from the fresh results in reports/allure-results/ — no manual allure generate step needed.

Open the report after a run:

allure open reports/allure-report

(or allure serve reports/allure-results to build and open in one step.)

If the allure command isn't found on your PATH, the run still completes and raw results still land in reports/allure-results/ - the framework logs a warning instead of failing the run, and you can generate the HTML report manually once Allure is installed.

Logs

Each test run writes a fresh log file to logs/test_run_<timestamp>.log, in addition to echoing to the console.

Screenshots

Any failing test automatically has a screenshot saved to screenshots/ and attached to the Allure report, along with the page URL and full page source at the moment of failure — useful for debugging without needing to rerun the test.

Adding Test Data

  • CSV: drop a .csv file into test_data/ and read it with utils.data_reader.read_csv_data("your_file.csv").
  • Excel: drop a .xlsx file into test_data/ and read it with utils.data_reader.read_excel_data("your_file.xlsx", sheet_name="Sheet1") (omit sheet_name to use the active sheet).
  • JSON: drop a .json file into test_data/ and read it with utils.data_reader.read_json_data("your_file.json"). A list root is returned as-is; a single object root is wrapped in a one-item list.
  • YAML: drop a .yaml/.yml file into test_data/ and read it with utils.data_reader.read_yaml_data("your_file.yaml") (same wrapping rule as JSON).
  • All four return the same shape (list[dict], one dict per row/record), so they can be fed straight into @pytest.mark.parametrize. Or call utils.data_reader.read_test_data("your_file.<ext>") and let it pick the right reader from the file extension automatically.