Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding pytest Fixtures and conftest.py for Test Setup

Tech 3

Fixtures in pytest alow defining reusable setup and teardown logic for test cases, serving as shared resources across tests.

Fixture Usage as Function Parameters

Fixtures can be passed directly as parameters to test functions:

import pytest

@pytest.fixture()
def setup_action():
    print('\nInitializing test setup')

def test_case_a(setup_action):
    print('--- Executing test case A ---')

class TestSuite:
    def test_case_b(self, setup_action):
        print('--- Executing test case B ---')

if __name__ == '__main__':
    pytest.main(["-s", "-v", "-q", "test_fixtures.py"])

Applying Fixtures with @pytest.mark.usefixtures

The @pytest.mark.usefixtures decorator attaches fixtures to test functions or classes:

import pytest

@pytest.fixture()
def setup_action():
    print('\nInitializing test setup')

@pytest.mark.usefixtures('setup_action')
def test_case_a():
    print('--- Executing test case A ---')

@pytest.mark.usefixtures('setup_action')
class TestSuite:
    def test_case_b(self):
        print('--- Executing test case B ---')

    def test_case_c(self):
        print('--- Executing test case C ---')

if __name__ == '__main__':
    pytest.main(["-s", "-v", "-q", "test_fixtures_decorator.py"])

Key Differences Between Parameter Passing and uesfixtures:

  • When a fixture returns data, usefixtures cannot access the return value, while parameter passing allows direct usage.
  • For fixtures with return values, pass them as parameters; to fixtures without returns, either method works.

Fixture Configuration Parameters

Fixtures support several configuration options:

import pytest

param_list = ["data1", "data2"]

@pytest.fixture(scope="function", params=param_list, autouse=True, ids=['scenario1', 'scenario2'], name='fixture_example')
def configured_fixture(request):
    print(request.param)

def test_scenario_one():
    print("\nRunning first test scenario")

def test_scenario_two():
    print("\nRunning second test scenario")

if __name__ == '__main__':
    pytest.main(["-s", "-v", "-q", "test_fixture_params.py"])

Parameter Descriptions:

  • scope: Defines fixture lifetime - "function" (default), "class", "module", or "session".
  • params: List of values that trigger multiple fixture executions, generating separate test cases.
  • autouse: When True, fixtures automatically apply to all tests in scope; default is False.
  • ids: Custom identifiers for parameterized tests; auto-generated if not specified.
  • name: Custom fixture name; useful for avoiding naming conflicts.
  • session scope: Fixture persists throughout the antire test run.

Sharing Fixtures with conftest.py

The conftest.py file enables fixture sharing across multiple test modules, useful for global setup like authentication or database connections.

Project Structure:

project_root/
├── conftest.py
├── test_module_a.py
└── test_module_b.py

conftest.py Contents:

import pytest
from selenium import webdriver
import requests
import time

@pytest.fixture(scope="session")
def session_setup():
    driver = webdriver.Chrome()
    driver.implicitly_wait(30)
    base_url = "http://www.example.com/"
    session = requests.Session()
    
    yield driver, session, base_url
    
    print('Closing browser driver')
    driver.quit()
    print('Closing request session')
    time.sleep(2)
    session.close()

@pytest.fixture(scope="function", autouse=True)
def database_connection():
    print('Establishing database connection')
    pass

def pytest_configure(config):
    config.addinivalue_line(
        "markers", "web_test"
    )

Test Module Example:

import pytest
import time

@pytest.mark.web_test
class WebTestSuite:
    @pytest.mark.parametrize('search_input,expected_result', [('test', 'test'), ('example', 'example')])
    def test_search_functionality(self, session_setup, search_input, expected_result):
        driver, session, base_url = session_setup
        time.sleep(2)
        driver.get(base_url)
        driver.find_element_by_id("search_box").send_keys(search_input)
        driver.find_element_by_id("search_button").click()
        time.sleep(2)
        result = driver.find_element_by_xpath('//*[@id="result_1"]/h3/a').get_attribute('innerHTML')
        print(result)
        assert expected_result in result

if __name__ == '__main__':
    pytest.main([])

Additional Test Module:

class AnotherTestSuite:
    def test_using_shared_fixture(self, session_setup):
        print('\nTest data: {}'.format(session_setup))

Execute tests from the project root with:

pytest -s -q --tb=no

Observations:

  • The database_connection fixture executes for each test function due to scope="function" and autouse=True.
  • The session_setup fixture runs once per test sesssion.
  • The yield statement separates pre-test setup (before yield) from post-test teardown (after yield).
Tags: pytest

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.