Skip to main content

Generate pytest and unittest parameters from YAML configuration files.

Project description

yaml-test-params

yaml-test-params logo

PyPI - Python Version License: MIT

A Python library for dynamic test parameter generation from YAML configuration files. This library enables flexible, data-driven test scenarios by combining Pydantic models with pytest's parametrize functionality or Python's built-in unittest library.

Features

  • Configuration-driven tests: Define test parameters in YAML files instead of hardcoding them
  • Pydantic validation: Type-safe configuration models with automatic validation
  • Automatic test expansion: Range configurations are automatically expanded into individual test cases
  • Seamless pytest integration: Works with pytest's native parametrize mechanism
  • unittest support: Generated parameter sets can be iterated in standard unittest.TestCase tests
  • Flexible parameter types: Support for simple values, lists, and ranges
  • Custom test case data: test_cases can include any YAML data types accepted by your Pydantic models

Installation

uv add yaml-test-params

Dependencies:

  • Python >= 3.9
  • pydantic >= 2.0
  • pyyaml >= 6.0.2

For local development and examples, install the development extra:

uv sync --extra dev

How It Works

The project supports dynamic parameter generation for tests from configuration files, enabling flexible test scenarios.

Workflow

  1. Base Pydantic models define the structure of test cases and the YAML configuration file structure.
  2. YAML configuration defines test parameters and scenarios.
  3. Test runner integration uses the generated arguments either through a pytest_generate_tests hook or directly inside unittest.TestCase.

Quick Start

Step 1: Define Your Models

Create Pydantic models that represent your test case structure:

from yaml_test_params.models import (
    BaseTestCase,
    BaseTestConfig,
    BaseTestConfigCollection,
    ParametrizeInteger,
    ParametrizeString,
)


class ExampleTestCase(BaseTestCase):
    test_name: str
    integer: ParametrizeInteger
    string: ParametrizeString

    @property
    def arg_id(self) -> str:
        return self.test_name


class ExampleTestConfig(BaseTestConfig):
    test_cases: list[ExampleTestCase]


class ExampleTestConfigCollection(BaseTestConfigCollection):
    collection: list[ExampleTestConfig]

Step 2: Create YAML Configuration

Define your test parameters in a YAML file:

collection:
  - name: examples
    test_cases:
      - test_name: int_1,2,3__str_a
        integer:
          values: [1, 2, 3]
        string: a

      - test_name: int_42__str_a,b,c
        integer: 42
        string:
          values: [a, b, c]

      - test_name: int_1_10_1__str_a
        integer:
          from: 1
          to: 10
          step: 1
        string: a

      - test_name: int_1_10_2__str_a,b,c
        integer:
          from: 1
          to: 10
          step: 2
        string:
          values: [a, b, c]

Step 3: Use Generated Parameters in Tests

You can use the generated parameters with either pytest or unittest.

Option A: Configure pytest Hook

Add the hook to your conftest.py:

import pytest

from yaml_test_params.args_loader import load_parametrize_args
from ..models import ExampleTestConfigCollection


def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
    test_cls = getattr(metafunc, "cls", None)
    if test_cls is None:
        return

    full_cls_name = f"{test_cls.__module__}.{test_cls.__qualname__}"

    if (
        full_cls_name == "examples.pytest_tests.test_examples.TestParametrizeExamples"
        and metafunc.function.__name__ == "test_values"
    ):
        parametrize_args = load_parametrize_args(
            path_to_configs="examples/collection.yaml",
            config_collection_model=ExampleTestConfigCollection,
            collection_name="examples",
        )
        needed_args = parametrize_args.keys_set
        if needed_args.issubset(set(metafunc.fixturenames)):
            metafunc.parametrize(**parametrize_args.to_dict())

Then write tests that accept the parameters defined in your configuration:

class TestParametrizeExamples:
    """Test class demonstrating pytest parametrize with integer and string variables."""

    def test_values(self, test_name: str, integer: int, string: str):
        """Test that integer and string parameters are correctly passed."""
        print(f"\n==============\n")
        print(f"Test name: {test_name}")
        print(f"integer: {integer}\nstring: {string}")

Run the pytest example:

uv run pytest -s examples/pytest_tests

Option B: Use unittest

Load the generated arguments once and iterate over them in a unittest.TestCase:

import unittest

from yaml_test_params.args_loader import load_parametrize_args
from ..models import ExampleTestConfigCollection


parametrize_args = load_parametrize_args(
    path_to_configs="examples/collection.yaml",
    config_collection_model=ExampleTestConfigCollection,
    collection_name="examples",
)


class TestParametrizeExamples(unittest.TestCase):
    """Test class demonstrating unittest with generated YAML parameters."""

    cases = parametrize_args.argvalues

    def test_values(self):
        """Test that integer and string parameters are correctly passed."""
        for test_name, integer, string in self.cases:
            with self.subTest(test_name=test_name, integer=integer, string=string):
                print(f"\n==============\n")
                print(f"Test name: {test_name}")
                print(f"integer: {integer}\nstring: {string}")

Run the unittest example:

uv run python -m unittest examples.unittest_tests.test_examples

See the full examples in examples/pytest_tests and examples/unittest_tests.

Configuration Types

The library supports three types of parameter configurations:

test_cases may also contain any additional fields and data types that can be represented in YAML and validated by your Pydantic models, such as booleans, lists, dictionaries, nested models, dates, or enums. These fields are passed to generated test arguments according to the model definition.

The built-in parametrization config models currently expand only int and str values through ParametrizeInteger and ParametrizeString.

class ExampleTestCase(BaseTestCase):
    integer: ParametrizeInteger
    string: ParametrizeString
    list_of_types: list[int, str, float]
test_cases:
  - test_name: int_1_10_2__str_a,b,c__42_abc_0.07
    integer:
        from: 1
        to: 10
        step: 2
    string:
        values: [a, b, c]
    list_of_types: [42, abc, 0.07]

Simple Value

A single value for a parameter:

integer: 42
string: "hello"

List of Values

Multiple discrete values:

integer:
  values: [1, 2, 3]
string:
  values: [a, b, c]

Range

A range of values with start, end, and step:

integer:
  from: 1
  to: 10
  step: 2

This generates values: [1, 3, 5, 7, 9].

step must be a positive integer.

Available Models

ValueConfig

Configuration for parameters with a simple value:

class ValueConfig(BaseModel):
    value: int | str

ListConfig

Configuration for parameters with a list of values:

class ListConfig(BaseModel):
    values: list[int | str]

RangeConfig

Configuration for parameters with a range:

class RangeConfig(BaseModel):
    from_: int
    to: int
    step: int

Type Aliases

ParametrizeIntegerConfigModels = RangeConfig | ListConfig | ValueConfig
ParametrizeStringConfigModels = ListConfig | ValueConfig
ParametrizeInteger = int | ParametrizeIntegerConfigModels
ParametrizeString = str | ParametrizeStringConfigModels

Base Classes

class BaseTestCase(BaseModel, ABC):
    test_name: str

class BaseTestConfig(BaseModel):
    name: str
    test_cases: list[BaseTestCase]

class BaseTestConfigCollection(BaseModel):
    collection: list[BaseTestConfig]

API Reference

load_parametrize_args()

Loads and parses a YAML configuration file and returns parametrize arguments.

def load_parametrize_args(
    path_to_configs: Union[pathlib.Path, str],
    config_collection_model: Type[TestConfigCollection],
    collection_name: str,
) -> ParametrizeArgs:

Parameters:

Parameter Type Description
path_to_configs pathlib.Path | str Path to the YAML configuration file
config_collection_model Type[TestConfigCollection] Pydantic model class for parsing the configuration
collection_name str Name of the test collection to use from the configuration

Returns: ParametrizeArgs object containing parametrize arguments

Raises: ValueError if no configuration is found for the given collection name

ParametrizeArgs

Dataclass holding generated test parameters for pytest and unittest integrations:

@dataclass
class ParametrizeArgs:
    argnames: str | None = None
    argvalues: list[tuple] = field(default_factory=list)
    ids: list[str] = field(default_factory=list)

Methods:

Method Description
init_arg_names(model_cls) Initialize argument names from a Pydantic model
add_params(arg_id, arg_values) Add a parameterized test case
to_dict() Convert to dictionary for metafunc.parametrize()
keys Property returning the tuple of argument keys
keys_set Property returning the set of argument keys

Exported Symbols

__all__ = [
    "BaseTestCase",
    "BaseTestConfig",
    "BaseTestConfigCollection",
    "ListConfig",
    "ParametrizeArgs",
    "ParametrizeInteger",
    "ParametrizeIntegerConfigModels",
    "ParametrizeString",
    "ParametrizeStringConfigModels",
    "RangeConfig",
    "ValueConfig",
    "load_parametrize_args",
]

Project Structure

yaml-test-params/
├── examples/
│   ├── collection.yaml
│   ├── models.py
│   ├── pytest_tests/
│   │   ├── conftest.py
│   │   └── test_examples.py
│   └── unittest_tests/
│       └── test_examples.py
├── tests/
│   ├── test_args_loader.py
│   ├── test_models.py
│   └── test_parametrize_args.py
├── yaml_test_params/
│   ├── __init__.py
│   ├── args_loader.py          # YAML configuration loader
│   ├── models.py               # Pydantic model definitions
│   └── parametrize_args.py     # Parametrize arguments dataclass
├── CONTRIBUTING.md
├── LICENSE.txt
├── pyproject.toml
└── README.md

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yaml_test_params-0.1.1.tar.gz (1.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yaml_test_params-0.1.1-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file yaml_test_params-0.1.1.tar.gz.

File metadata

  • Download URL: yaml_test_params-0.1.1.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.21

File hashes

Hashes for yaml_test_params-0.1.1.tar.gz
Algorithm Hash digest
SHA256 2df83fb50b1c0657ee05db186cedd0c0848403bbeeb6e7026164b405c7675baa
MD5 8a8584e0266e22edb9c066c2c173520b
BLAKE2b-256 572edecd4d5a1cfce2170625ebdf9aa1a66b5ffad8b5a8e428097f73dc74283e

See more details on using hashes here.

File details

Details for the file yaml_test_params-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for yaml_test_params-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 994529e3e59a9aff4604eec34f462e9a9058992829d0f4ec37195883e7dd7f18
MD5 aee5eb4034cbb73b080a92af098262bc
BLAKE2b-256 46b73da7aaebfd2b167f81f3d28da84019ca44d210a6215421b1b2ace18d2c7c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page