Configure Playwright Python Tests: pytest.ini, pyproject.toml and conftest.py

Playwright with TypeScript has a single playwright.config.ts file. In Python, configuration is split between two places that every pytest user knows: a settings file (pytest.ini or pyproject.toml) for default command-line options, and conftest.py for Python code that customises the browser, context and timeouts. In this lesson you will build a complete, realistic configuration step by step and verify each setting with a test.

Advertisement

πŸ“– What You’ll Learn: where pytest looks for configuration, addopts, testpaths and base_url in pytest.ini and pyproject.toml, how addopts combines with command-line options, overriding browser_context_args and browser_type_launch_args in conftest.py, per-test context settings with a marker, and setting default timeouts for actions and assertions.


πŸ—ΊοΈ Where Configuration Lives

What you want to configureWhere it goesExample
Default command-line optionspytest.ini or pyproject.toml (addopts)--browser chromium --tracing retain-on-failure
Where tests live, base URLpytest.ini or pyproject.tomltestpaths = tests, base_url = https://...
Browser launch settingsconftest.py fixture browser_type_launch_argsextra Chromium flags
Context settings (viewport, locale, timezone, HTTPS errors)conftest.py fixture browser_context_args{"viewport": {"width": 1440, "height": 900}}
Timeoutsconftest.pyexpect.set_options(timeout=10_000)

pytest finds its settings file by looking in the folder you run it from and then in parent folders. Put the settings file and conftest.py in your project’s root folder, next to the tests folder.


πŸ“„ Option 1: pytest.ini

Create pytest.ini in the project root:

πŸ“„ pytest.ini

[pytest]
# Look for tests only in the tests folder
testpaths = tests

# Options added to every pytest run
addopts = -v --tracing retain-on-failure --screenshot only-on-failure

# Relative URLs in page.goto() are resolved against this
base_url = https://sqatools.in
  • [pytest] is the required section header.
  • testpaths = tests makes a bare pytest command search only the tests folder, which is faster and avoids collecting stray files.
  • addopts is added in front of whatever you type, so every run now uses verbose output and keeps traces and screenshots of failures.
  • base_url lets tests call page.goto("/automation-practice-page/"). A --base-url on the command line takes priority over this value.
  • Lines starting with # are comments.

πŸ“„ Option 2: pyproject.toml

Many Python projects keep all tool settings in pyproject.toml. The same configuration looks like this:

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tracing retain-on-failure --screenshot only-on-failure"
base_url = "https://sqatools.in"
  • The section is [tool.pytest.ini_options], and values follow TOML rules: strings in quotes, lists in square brackets.
  • pytest 9 also accepts a native [tool.pytest] table, where addopts is a list: addopts = ["-v", "--tracing", "retain-on-failure"]. The ini_options form works on every pytest version, so it is the safer choice for shared projects.

⚠️ Watch out: Use only one settings file. If both pytest.ini and a [tool.pytest.ini_options] section exist, pytest.ini wins and the pyproject settings are ignored. The only sign is a short note in the run header: configfile: pytest.ini (WARNING: ignoring pytest config in pyproject.toml!). Check the configfile: line whenever settings seem to have no effect.


βš–οΈ How addopts Combines with the Command Line

addopts does not replace what you type; it is added before it. For options that take one value, the last one wins, so the command line overrides the file. But --browser can be repeated, so the values add up. To see it, imagine addopts = --browser firefox in pytest.ini and this small test:

def test_which_browser(page, browser_name):
    print("RUN", browser_name)

⌨️ Terminal

pytest -q -s --browser webkit

πŸ–₯️ Output

RUN firefox
RUN webkit
2 passed in 2.04s

The test ran in both Firefox (from the file) and WebKit (from the command line). If you want to switch browsers freely from the command line, do not put --browser in addopts; Chromium is already the default.


🐍 conftest.py: Python-Powered Configuration

conftest.py is a special pytest file. Fixtures defined in it are available to every test in its folder and sub-folders without importing anything. pytest-playwright builds its browser, context and page fixtures from a few settings fixtures, and you customise them by redefining those settings fixtures in conftest.py.

Create conftest.py in the project root:

πŸ“„ conftest.py

import pytest
from playwright.sync_api import Page, expect


@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
    """Settings applied to every new browser context (and so every page)."""
    return {
        **browser_context_args,                        # keep the plugin's own settings
        "viewport": {"width": 1440, "height": 900},    # larger desktop window
        "locale": "en-GB",                             # browser language
        "timezone_id": "Europe/London",                # browser time zone
        "ignore_https_errors": True,                   # accept self-signed test certificates
    }


@pytest.fixture(scope="session")
def browser_type_launch_args(browser_type_launch_args):
    """Settings used when the browser process is launched."""
    return {
        **browser_type_launch_args,
        "slow_mo": 0,                                  # keep 0 in CI; raise it to watch tests
    }


@pytest.fixture(autouse=True)
def default_timeouts(page: Page):
    """Runs before every test: make actions and navigations time out after 15 s."""
    page.set_default_timeout(15_000)
    page.set_default_navigation_timeout(30_000)


# Web-first assertions (expect) wait 5 s by default; give slow pages 10 s
expect.set_options(timeout=10_000)

Line by line:

  1. **browser_context_args** is a session-scoped fixture provided by pytest-playwright. Your version receives the original as a parameter (also named browser_context_args), and returns a new dictionary that starts with **browser_context_args so the plugin’s own settings (such as the --base-url and --device values) are kept, then adds yours.
  2. The keys are the same as the parameters of browser.new_context(): viewport, locale, timezone_id, ignore_https_errors, user_agent, color_scheme, geolocation, permissions, storage_state, and more.
  3. **scope="session"** must match the plugin’s fixture, because the settings are calculated once for the whole run.
  4. **browser_type_launch_args** works the same way for launch() options such as slow_mo, args (extra browser command-line flags) or downloads_path.
  5. **autouse=True** runs the default_timeouts fixture for every test automatically. It asks for page, so it changes the timeouts of the very page your test receives.
  6. **expect.set_options(timeout=10_000)** changes the default wait for every expect() assertion. Because conftest.py is imported before the tests run, it applies everywhere.

πŸ’‘ Tip: 15_000 is the same number as 15000. Python ignores the underscores; they just make large numbers easier to read.


βœ… Prove the Configuration Works

Create tests/test_config.py:

πŸ“„ tests/test_config.py

from playwright.sync_api import Page, expect


def test_context_settings_are_applied(page: Page):
    page.goto("/automation-practice-page/")    # relative URL thanks to base_url

    assert page.viewport_size == {"width": 1440, "height": 900}
    assert page.evaluate("navigator.language") == "en-GB"
    assert page.evaluate("Intl.DateTimeFormat().resolvedOptions().timeZone") == "Europe/London"


def test_base_url_is_used(page: Page):
    page.goto("/automation-practice-page/")
    expect(page).to_have_url("https://sqatools.in/automation-practice-page/")

Run pytest with no arguments:

⌨️ Terminal

pytest

πŸ–₯️ Output

configfile: pytest.ini
testpaths: tests
plugins: playwright-0.10.0
collecting ... collected 3 items

tests/test_config.py::test_context_settings_are_applied[chromium] PASSED [ 33%]
tests/test_config.py::test_base_url_is_used[chromium] PASSED             [ 66%]
tests/test_markers.py::test_dark_mode_for_one_test[chromium] PASSED      [100%]
  • configfile: pytest.ini and testpaths: tests confirm which settings file pytest used.
  • The -v style output appears without typing -v, because it is in addopts.
  • The plugins line on your machine may list different plugins; that is fine.

The same tests also pass in Firefox, which shows the settings are browser-independent:

⌨️ Terminal

pytest --browser firefox

🎯 Different Settings for One Test

Sometimes one test needs something special, such as dark mode or a different language. pytest-playwright provides a marker for that. Create tests/test_markers.py:

πŸ“„ tests/test_markers.py

import pytest
from playwright.sync_api import Page


@pytest.mark.browser_context_args(color_scheme="dark", locale="fr-FR")
def test_dark_mode_for_one_test(page: Page):
    page.goto("/automation-practice-page/")

    assert page.evaluate("matchMedia('(prefers-color-scheme: dark)').matches") is True
    assert page.evaluate("navigator.language") == "fr-FR"
  • @pytest.mark.browser_context_args(...) adds or overrides context settings for this test only.
  • Settings from conftest.py that the marker does not mention (here the 1440×900 viewport) still apply.
  • matchMedia(...) is a browser API; the test asks the page whether it is in dark mode.

πŸ—‚οΈ The Project Structure

πŸ–₯️ Output

playwright-python-demo/
β”œβ”€β”€ .venv/
β”œβ”€β”€ conftest.py          # fixtures: context args, launch args, timeouts
β”œβ”€β”€ pytest.ini           # defaults: testpaths, addopts, base_url
β”œβ”€β”€ requirements.txt
└── tests/
    β”œβ”€β”€ test_config.py
    └── test_markers.py

πŸ” TypeScript Config vs Python Config

If you know Playwright with TypeScript, this table maps the familiar playwright.config.ts settings to Python:

playwright.config.tsPython equivalent
testDir: './tests'testpaths = tests
use: { baseURL }base_url in pytest.ini, or --base-url
use: { viewport, locale, timezoneId }browser_context_args fixture in conftest.py
use: { trace: 'retain-on-failure' }--tracing retain-on-failure in addopts
expect: { timeout: 10000 }expect.set_options(timeout=10_000)
use: { actionTimeout }page.set_default_timeout() in an autouse fixture
projects: [...]--browser (repeatable) or --device
retries: 2pytest-rerunfailures plugin: --reruns 2
workers: 4pytest-xdist plugin: -n 4

❓ Frequently Asked Questions

πŸ€” Can I have more than one conftest.py?

Yes. A conftest.py in a sub-folder adds or overrides fixtures for the tests in that folder only. The root conftest.py applies to everything. You will use this in Sharing Fixtures with conftest.py.

πŸ€” Why does my browser_context_args override seem to do nothing?

The most common causes are a missing scope="session", forgetting to return the dictionary, or a key misspelled (for example timezone instead of timezone_id). Run a test that prints page.viewport_size or navigator.language to confirm.

πŸ€” Where do I set the browser headless mode in config?

Headless is the default. Add --headed to addopts if you want windows on your own machine, but do not commit that, because CI servers have no screen.

πŸ€” Should I put secrets like passwords in pytest.ini?

No. Keep secrets in environment variables or a .env file that is not committed. You will learn this in Environment Variables and .env Files.


🏁 Summary

  • pytest.ini or pyproject.toml holds testpaths, addopts and base_url; use only one settings file.
  • addopts is added before command-line options, and repeatable options like --browser add up.
  • Override browser_context_args and browser_type_launch_args in conftest.py (session scope, keep the original with **).
  • Use an autouse fixture for action timeouts, expect.set_options() for assertion timeouts, and the browser_context_args marker for one-off settings.

Next, you will let Playwright write tests for you with Codegen, and learn how to turn recorded code into clean pytest tests.

🎬 Watch the Playwright tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on the SQA Tools Automation Practice Page

2 thoughts on “Configure Playwright Python Tests: pytest.ini, pyproject.toml and conftest.py”

Leave a Comment