import jsonfrom pathlib import Pathimport pytest@pytest.fixture(scope="session")def users(): """ Loads testData/users.json and returns a dict. Accessible in any test as the 'users' fixture. """ root = Path(__file__).parent.parent # go from /tests to project root data_path = root / "testData" / "users.json" with data_path.open(encoding="utf-8") as f: return json.load(f)
import osfrom pathlib import Pathimport pytestfrom dotenv import load_dotenv# Load .env file at module levelload_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")@pytest.fixture(scope="session")def creds(): """ Provides credentials from environment variables. Fails early with a clear message if missing. """ user = os.getenv("USERNAME") pwd = os.getenv("PASSWORD") if not user or not pwd: raise RuntimeError("Missing USERNAME/PASSWORD in environment. ") return {"valid_user": user, "pwd": pwd}
Loading .env File
from dotenv import load_dotenvload_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")
Called at module level (runs when conftest.py is imported)
Loads variables from .env into os.environ
Explicit path ensures correct file is loaded
Accessing Variables
user = os.getenv("USERNAME")pwd = os.getenv("PASSWORD")
os.getenv() returns None if variable not found
Better than os.environ["KEY"] which raises KeyError
Can provide default: os.getenv("API_URL", "https://api.example.com")
Error Handling
if not user or not pwd: raise RuntimeError("Missing USERNAME/PASSWORD in environment. ")
Why fail early?
Tests won’t run with invalid setup
Clear error message for developers
Prevents cryptic failures later in test execution
Return Structure
return {"valid_user": user, "pwd": pwd}
Returns a dictionary for consistent access pattern:
# In testcreds["valid_user"] # Always workscreds["pwd"] # Always works
Use JSON for data, environment variables for config:
@pytest.fixture(scope="session")def test_config(): return { "base_url": os.getenv("BASE_URL", "https://www.saucedemo.com"), "timeout": int(os.getenv("TIMEOUT", "30000")), "headless": os.getenv("HEADLESS", "true").lower() == "true" }@pytest.fixture(scope="session")def test_users(): data_path = Path(__file__).parent.parent / "testData" / "users.json" with data_path.open() as f: return json.load(f)def test_configurable_login(page, test_config, test_users): page.goto(test_config["base_url"]) # Use both fixtures together
Create helper functions to generate test data:
def build_user(username=None, password=None): return { "username": username or "standard_user", "password": password or "secret_sauce" }def test_with_builder(page): # Use default values user = build_user() # Or override specific fields invalid_user = build_user(password="wrong")
@pytest.fixture(scope="session")def users(): root = Path(__file__).parent.parent data_path = root / "testData" / "users.json" if not data_path.exists(): raise FileNotFoundError(f"Test data not found: {data_path}") with data_path.open(encoding="utf-8") as f: data = json.load(f) # Validate structure required_keys = ["validUser", "invalidUser"] for key in required_keys: if key not in data: raise ValueError(f"Missing required key in users.json: {key}") return data
Document Data Structure
Add comments to JSON (if using JSON5) or in fixture docstrings: