Pytest plugin for coordinating the order in which marked tests run.
Reason this release was yanked:
Just a test
Project description
pytest-conductor
A pytest plugin that allows you to control the order in which tests run based on their tags (markers) or fixtures. Perfect for coordinating test execution in CI/CD pipelines, managing test dependencies, and optimizing test suite performance.
โจ Features
- ๐ฏ Tag-based Ordering: Order tests by pytest markers (fast โ slow โ integration)
- ๐ง Fixture-based Ordering: Order tests by the fixtures they use (db โ redis โ cache)
- โก Unmatched Test Handling: Control how tests without matching tags/fixtures are handled
- ๐ซ Skip Unmatched Tests: Option to skip tests that don't match the order list entirely
- ๐ก๏ธ Error Validation: Automatic validation of fixture availability for reliable ordering
- ๐ Performance Optimized: Minimal overhead with efficient sorting algorithms
- ๐ Comprehensive Testing: Full test suite with unit tests and integration tests
- ๐ญ Interactive Demo: Built-in demo showing all features with detailed logging
Installation
pip install pytest-conductor
๐ญ Quick Demo
See pytest-conductor in action with our interactive demo:
# Clone the repository
git clone https://github.com/your-username/pytest-conductor.git
cd pytest-conductor
# Run the interactive demo
hatch run demo
The demo shows:
- Tag ordering (fast โ slow โ integration)
- Fixture ordering (basic_calculator โ advanced_calculator โ sample_data)
- Unmatched test handling (first, last, none)
- Error handling for invalid configurations
๐ Example Project
The repository includes a complete example project demonstrating real-world usage:
# Navigate to the example project
cd example
# Install dependencies and pytest-conductor
hatch run pip install -e ../
# Run tests with coordination
hatch run pytest --tag-order fast slow -v
hatch run pytest --fixture-order basic_calculator advanced_calculator --ordering-mode fixture -v
The example project includes:
- Calculator application with basic and advanced operations
- Comprehensive test suite with different tags and fixtures
- Global and local fixtures demonstrating scope validation
- Integration tests showing all plugin features
Usage
Ordering Modes
The plugin supports two ordering modes:
- Mark Mode (default): Order tests by their pytest markers/tags
- Fixture Mode: Order tests by the fixtures they use
Mark Mode - Basic Tag Ordering
Use the --tag-order option to specify the order in which tags should run:
pytest --tag-order fast slow integration
This will run all tests with the fast tag first, then slow tests, then integration tests.
Fixture Mode - Basic Fixture Ordering
Use the --fixture-order option to specify the order in which fixtures should run:
pytest --fixture-order db redis cache --ordering-mode fixture
This will run all tests that use the db fixture first, then tests using redis, then tests using cache.
โ ๏ธ Important Limitation: Fixture ordering only works with fixtures that are globally available to all tests. The plugin will throw an error if you try to order by a fixture that is not available to all tests in your test suite. This ensures reliable ordering behavior.
Error Handling and Validation
The plugin includes robust error handling to prevent configuration issues:
Fixture Availability Validation
When using fixture ordering, the plugin validates that all specified fixtures are available to all tests:
# This will fail if 'nonexistent_fixture' is not available to all tests
pytest --fixture-order nonexistent_fixture --ordering-mode fixture
Error Message: ValueError: Fixtures not available to all tests: nonexistent_fixture. Fixture ordering requires all fixtures to be globally available. Make sure these fixtures are defined in a conftest.py file that is accessible to all tests, or use mark ordering instead.
Best Practices
- Use global fixtures defined in a root-level
conftest.pyfile - Use mark ordering for tests with local or conditional fixtures
- Test your configuration with
--collect-onlyto catch issues early
Handling Unmatched Tests
Use the --unmatched-order option to control how tests without matching tags/fixtures are handled:
# Run unmatched tests first
pytest --tag-order fast slow --unmatched-order first
pytest --fixture-order db redis --ordering-mode fixture --unmatched-order first
# Run unmatched tests last
pytest --tag-order fast slow --unmatched-order last
pytest --fixture-order db redis --ordering-mode fixture --unmatched-order last
# Run unmatched tests in any order (default)
pytest --tag-order fast slow --unmatched-order any
pytest --fixture-order db redis --ordering-mode fixture --unmatched-order any
# Skip unmatched tests entirely
pytest --tag-order fast slow --unmatched-order none
pytest --fixture-order db redis --ordering-mode fixture --unmatched-order none
New in v0.1.0: Skip Unmatched Tests
The --unmatched-order none option is a new feature that allows you to skip tests that don't match your specified order list entirely. This is useful when you want to run only a specific subset of tests:
# Run only fast and slow tests, skip all others
pytest --tag-order fast slow --unmatched-order none
# Run only tests using specific fixtures, skip all others
pytest --fixture-order db redis --ordering-mode fixture --unmatched-order none
Example Test Structure
Mark Mode Example
import pytest
@pytest.mark.fast
def test_fast_operation():
"""This test will run first when using --tag-order fast slow"""
assert True
@pytest.mark.slow
def test_slow_operation():
"""This test will run second when using --tag-order fast slow"""
assert True
def test_no_tags():
"""This test has no tags - behavior depends on --unmatched-order"""
assert True
@pytest.mark.fast
@pytest.mark.slow
def test_multiple_tags():
"""This test has multiple tags - uses the first one in the order"""
assert True
Fixture Mode Example
import pytest
@pytest.fixture
def db():
"""Database fixture."""
return {"type": "database"}
@pytest.fixture
def redis():
"""Redis fixture."""
return {"type": "redis"}
def test_db_operation(db):
"""This test will run first when using --fixture-order db redis"""
assert db["type"] == "database"
def test_redis_operation(redis):
"""This test will run second when using --fixture-order db redis"""
assert redis["type"] == "redis"
def test_no_fixtures():
"""This test has no fixtures - behavior depends on --unmatched-order"""
assert True
def test_multiple_fixtures(db, redis):
"""This test has multiple fixtures - uses the first one in the order"""
assert db["type"] == "database"
assert redis["type"] == "redis"
Command Line Options
--tag-order TAG1 TAG2 ...: Specify the order of tags for test execution (mark mode)--fixture-order FIXTURE1 FIXTURE2 ...: Specify the order of fixtures for test execution (fixture mode)--ordering-mode {mark,fixture}: Choose ordering mode (default: mark)--unmatched-order {any,first,last,none}: How to handle tests without matching tags/fixturesany: Run unmatched tests in any order (default)first: Run unmatched tests before tagged/fixture testslast: Run unmatched tests after tagged/fixture testsnone: Skip unmatched tests entirely
How It Works
Mark Mode
- The plugin extracts tags from test markers (pytest.mark)
- Tests are sorted based on the specified tag order
- Tests with multiple tags use the highest priority tag (first in the order)
- Tests without tags are handled according to the
--unmatched-ordersetting
Fixture Mode
- The plugin extracts fixture names from test function parameters
- Tests are sorted based on the specified fixture order
- Tests with multiple fixtures use the highest priority fixture (first in the order)
- Tests without fixtures are handled according to the
--unmatched-ordersetting
Edge Cases and Special Behavior
Multiple Tags/Fixtures
When a test has multiple tags or fixtures that are in your specified order, the plugin will:
- Run the test only once (no duplication)
- Use the highest priority tag/fixture (the one that appears first in your order list)
Example with Multiple Tags
@pytest.mark.fast
@pytest.mark.slow
def test_multiple_tags():
"""This test has both 'fast' and 'slow' tags."""
assert True
When running pytest --tag-order fast slow integration, this test will:
- Run once (not twice)
- Run in the fast group (since 'fast' comes first in the order)
Example with Multiple Fixtures
def test_multiple_fixtures(db, redis, cache):
"""This test uses multiple fixtures."""
assert True
When running pytest --fixture-order db redis cache --ordering-mode fixture, this test will:
- Run once (not multiple times)
- Run in the db group (since 'db' comes first in the order)
Conftest Fixtures
The plugin handles fixtures defined in conftest.py files the same way as regular fixtures:
- Global conftest fixtures (in root
conftest.py) are detected normally - Nested conftest fixtures (in subdirectory
conftest.pyfiles) are also detected - The plugin looks at the test function's parameter names, regardless of where the fixture is defined
โ ๏ธ Fixture Availability Requirement: For fixture ordering to work correctly, all fixtures in your --fixture-order list must be available to all tests that might use them. The plugin will throw an error if any fixture in your order list is not available to all tests, ensuring reliable ordering behavior.
Example with Nested Conftest
tests/
โโโ conftest.py # global_fixture
โโโ unit/
โ โโโ conftest.py # unit_fixture
โ โโโ test_unit.py # uses both global_fixture and unit_fixture
โโโ integration/
โโโ conftest.py # integration_fixture
โโโ test_integration.py # uses global_fixture and integration_fixture
When running pytest --fixture-order global_fixture unit_fixture integration_fixture --ordering-mode fixture:
- Tests in
unit/will run first if they useglobal_fixtureorunit_fixture - Tests in
integration/will run first if they useglobal_fixtureorintegration_fixture - The plugin doesn't need to know where the fixture is defined - it just looks at the test parameters
Handling Deeply Nested Conftest Fixtures
For deeply nested directory structures, the plugin works seamlessly:
tests/
โโโ conftest.py # global_fixture
โโโ api/
โ โโโ conftest.py # api_fixture
โ โโโ v1/
โ โ โโโ conftest.py # v1_fixture
โ โ โโโ test_endpoints.py # uses global_fixture, api_fixture, v1_fixture
โ โโโ v2/
โ โโโ conftest.py # v2_fixture
โ โโโ test_endpoints.py # uses global_fixture, api_fixture, v2_fixture
โโโ database/
โโโ conftest.py # db_fixture
โโโ mysql/
โ โโโ conftest.py # mysql_fixture
โ โโโ test_queries.py # uses global_fixture, db_fixture, mysql_fixture
โโโ postgresql/
โโโ conftest.py # postgres_fixture
โโโ test_queries.py # uses global_fixture, db_fixture, postgres_fixture
Key Points:
- No special configuration needed - the plugin automatically detects all fixtures used by tests
- Fixture scope doesn't matter - whether fixtures are session, module, class, or function scope
- Conftest inheritance works - fixtures from parent directories are available to child tests
- Ordering is based on test parameters - not fixture definitions
Best Practices for Complex Fixture Structures:
- Use descriptive fixture names that indicate their purpose (e.g.,
mysql_db,redis_cache) - Consider using fixture prefixes to group related fixtures (e.g.,
api_v1_client,api_v2_client) - When ordering by fixtures, list them in the order you want tests to run
Unmatched Tests
Tests that don't have any of the specified tags or fixtures are handled according to the --unmatched-order setting:
any(default): Run unmatched tests in any orderfirst: Run unmatched tests before all tagged/fixture testslast: Run unmatched tests after all tagged/fixture testsnone: Skip unmatched tests entirely (they won't run)
Test Execution Order Guarantees
The plugin guarantees that:
- No test runs twice - even with multiple matching tags/fixtures
- Tests run in the specified order - within each priority group
- Unmatched tests are handled predictably - based on your
--unmatched-ordersetting - Fixture dependencies are respected - pytest's own fixture ordering still applies
Examples
Mark Mode Examples
Run fast tests first, then slow tests
pytest --tag-order fast slow
Run unit tests first, then integration tests, with untagged tests last
pytest --tag-order unit integration --unmatched-order last
Run smoke tests first, then full test suite
pytest --tag-order smoke full --unmatched-order last
Fixture Mode Examples
Run database tests first, then cache tests
pytest --fixture-order db cache --ordering-mode fixture
Run API tests first, then database tests, with tests without fixtures last
pytest --fixture-order api db --ordering-mode fixture --unmatched-order last
Run tests with expensive fixtures last
pytest --fixture-order simple expensive --ordering-mode fixture --unmatched-order first
Testing Edge Cases
The plugin includes comprehensive tests for edge cases. To verify the behavior:
Run Edge Case Tests
# Run the edge case test suite
pytest src/unit_tests/test_edge_cases.py -v
# Run practical examples
pytest src/unit_tests/test_edge_case_examples.py -v
Test Multiple Tags Ordering
# Test that tests with multiple tags run only once
pytest src/unit_tests/test_edge_case_examples.py --tag-order fast slow integration -v
Test Multiple Fixtures Ordering
# Test that tests with multiple fixtures run only once
pytest src/unit_tests/test_edge_case_examples.py --fixture-order db redis cache --ordering-mode fixture -v
Test Unmatched Test Handling
# Test unmatched tests running first
pytest src/unit_tests/test_edge_case_examples.py --tag-order fast slow --unmatched-order first -v
# Test unmatched tests running last
pytest src/unit_tests/test_edge_case_examples.py --tag-order fast slow --unmatched-order last -v
๐ ๏ธ Development
Installation
# Clone the repository
git clone https://github.com/your-username/pytest-conductor.git
cd pytest-conductor
# Install in development mode
pip install -e .
Test Structure
The project uses a comprehensive test structure:
src/
โโโ pytest_conductor/ # Main plugin code
โโโ unit_tests/ # Unit tests for the plugin itself
โ โโโ test_tag_ordering.py
โ โโโ test_fixture_ordering.py
โ โโโ test_fixture_validation.py
โ โโโ test_unmatched_none.py
โ โโโ ... (8 test files total)
โโโ integration_tests/ # Integration tests using the example project
โโโ test_pytest_conductor_integration.py
โโโ README.md
Running Tests
# Run unit tests only (test the plugin's core functionality)
hatch run unit-tests
# Run integration tests only (test with real-world example project)
hatch run integration-tests
# Run all tests
hatch run unit-tests && hatch run integration-tests
# Run the interactive demo (shows test coordination with detailed logging)
hatch run demo
Example Project Testing
# Navigate to example project
cd example
# Install pytest-conductor in the example environment
hatch run pip install -e ../
# Run example tests with coordination
hatch run pytest --tag-order fast slow -v
hatch run pytest --fixture-order basic_calculator advanced_calculator --ordering-mode fixture -v
๐ Project Structure
pytest-conductor/
โโโ src/
โ โโโ pytest_conductor/ # Main plugin code
โ โโโ unit_tests/ # Unit tests for the plugin
โ โโโ integration_tests/ # Integration tests with example project
โโโ example/ # Complete example project
โ โโโ src/
โ โ โโโ calculator/ # Example application
โ โ โโโ tests/ # Example tests with various tags/fixtures
โ โโโ pyproject.toml # Example project configuration
โโโ pyproject.toml # Main project configuration
โโโ README.md # This file
Marker Registration
To avoid warnings about unknown markers, you can register your custom markers in your pyproject.toml or pytest.ini file:
[tool.pytest.ini_options]
markers = [
"fast: marks tests as fast",
"slow: marks tests as slow",
"integration: marks tests as integration tests",
"unit: marks tests as unit tests",
]
Or in pytest.ini:
[tool:pytest]
markers =
fast: marks tests as fast
slow: marks tests as slow
integration: marks tests as integration tests
unit: marks tests as unit tests
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_conductor-0.1.3.tar.gz.
File metadata
- Download URL: pytest_conductor-0.1.3.tar.gz
- Upload date:
- Size: 30.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
692ad9a8cc5201705484b0d778112881471f4371d5a14e1839cb87f45e4598ee
|
|
| MD5 |
8ee4f189e241c158461ed04f306feda8
|
|
| BLAKE2b-256 |
54f47feb7a19221a1054ec5fe305b4fc5a72558ade5b5a14e7afdea8618ffb55
|
Provenance
The following attestation bundles were made for pytest_conductor-0.1.3.tar.gz:
Publisher:
release.yml on kassett/pytest-conductor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_conductor-0.1.3.tar.gz -
Subject digest:
692ad9a8cc5201705484b0d778112881471f4371d5a14e1839cb87f45e4598ee - Sigstore transparency entry: 332155392
- Sigstore integration time:
-
Permalink:
kassett/pytest-conductor@7806324a784a02cb6764856577300a831d4b0548 -
Branch / Tag:
refs/tags/0.1.18 - Owner: https://github.com/kassett
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7806324a784a02cb6764856577300a831d4b0548 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytest_conductor-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pytest_conductor-0.1.3-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9797ab9c07186cc8bf3dec25e988c779f907cf54a1c61f0a0bcd7483d184de4
|
|
| MD5 |
7c42a15ada891dd6ffabbbc688d0fca6
|
|
| BLAKE2b-256 |
50a5294f0908994a04629ba4b6eb8a726a4513bf31acc0672591966076eaaefb
|
Provenance
The following attestation bundles were made for pytest_conductor-0.1.3-py3-none-any.whl:
Publisher:
release.yml on kassett/pytest-conductor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_conductor-0.1.3-py3-none-any.whl -
Subject digest:
b9797ab9c07186cc8bf3dec25e988c779f907cf54a1c61f0a0bcd7483d184de4 - Sigstore transparency entry: 332155423
- Sigstore integration time:
-
Permalink:
kassett/pytest-conductor@7806324a784a02cb6764856577300a831d4b0548 -
Branch / Tag:
refs/tags/0.1.18 - Owner: https://github.com/kassett
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7806324a784a02cb6764856577300a831d4b0548 -
Trigger Event:
release
-
Statement type: