Skip to main content

bazis-test-utils

PyPI version Python Versions License

A utility package for testing in the Bazis ecosystem — a framework based on Django + FastAPI + Pydantic.

Quick Start

# Install the package
uv add bazis-test-utils

# Create test models
from bazis_test_utils.models_abstract import ParentEntityBase

class TestEntity(ParentEntityBase):
    pass

# Use in tests
from bazis_test_utils.factories_abstract import ParentEntityFactoryAbstract

class TestEntityFactory(ParentEntityFactoryAbstract):
    class Meta:
        model = TestEntity

Table of Contents

Description

bazis-test-utils provides base models and factories to simplify test writing in Bazis packages. The package includes standard models for testing hierarchical and dependent entities, as well as utilities for working with the FastAPI test client.

Features

  • Base Django test models:

    • ChildEntityBase — base model for child entities
    • DependentEntityBase — base model for dependent entities
    • ExtendedEntityBase — base model for extended entities
    • ParentEntityBase — base model for parent entities
  • Abstract factory classes for rapid test data generation with factory_boy integration

  • API testing utilities:

    • get_api_client() — function to create a FastAPI test client with optional authentication
    • parallel() — runs calls (requests) at once in threads, for the tests of the invariants under concurrent requests

Installation

uv add bazis-test-utils

Using pip

pip install bazis-test-utils

Development installation

git clone https://github.com/ecofuture-tech/bazis-test-utils.git
cd bazis-test-utils
uv sync --dev

Usage

Creating Test Models

In your project, create models by inheriting from the base classes:

from bazis_test_utils.models_abstract import ParentEntityBase, ChildEntityBase

class TestParentEntity(ParentEntityBase):
    """Model for testing parent entities"""
    pass

class TestChildEntity(ChildEntityBase):
    """Model for testing child entities"""
    pass

Using Factories

Create factories for your models using abstract factory classes:

from bazis_test_utils.factories_abstract import ParentEntityFactoryAbstract
from .models import TestParentEntity

class ParentEntityFactory(ParentEntityFactoryAbstract):
    class Meta:
        model = TestParentEntity

Then use them in your tests:

import pytest

@pytest.mark.django_db
def test_parent_entity_creation():
    parent = ParentEntityFactory.create()
    assert parent.id is not None
    assert parent.created_at is not None

Testing FastAPI Endpoints

Use get_api_client to create a test client:

# conftest.py
import pytest

@pytest.fixture(scope='function')
def sample_app():
    from sample.main import app
    return app

# test_sample_app.py
from bazis_test_utils.utils import get_api_client

def test_api_endpoint(sample_app):
    # Without authentication
    client = get_api_client(sample_app)
    response = client.get("/api/v1/entities/")
    assert response.status_code == 200

def test_authenticated_endpoint(sample_app):
    # With authentication token
    token = "your-test-token"
    client = get_api_client(sample_app, token=token)
    response = client.get("/api/v1/protected/")
    assert response.status_code == 200

Parallel requests

An invariant that must hold under concurrent requests ("no more than 3 steps per memo", a unique value) needs a test that sends the requests at once. parallel(count, func) calls func count times, parallel([f1, f2, ...]) each function once, every call in a thread of its own: the threads wait for each other before the calls, the results come in the order of the calls, each thread closes its database connections when its call is done, and the first exception of a call is raised in the test once all have ended. The endpoints see only committed data, so such a test uses transaction=True:

import pytest
from bazis_test_utils.utils import get_api_client, parallel


@pytest.mark.django_db(transaction=True)
def test_no_more_than_3_steps_per_memo(sample_app, memo):
    def add_step():
        return get_api_client(sample_app).post('/api/v1/catalog/step/', json_data={
            'data': {'type': 'catalog.step', 'relationships': {
                'memo': {'data': {'type': 'catalog.memo', 'id': str(memo.pk)}}}},
        }).status_code

    assert sorted(parallel(6, add_step)) == [201] * 3 + [422] * 3
    assert memo.steps.count() == 3

Two paths that create the same item (a custom action and the create route) race with parallel([lambda: by_action(...), lambda: by_create(...)]): the answers are 2xx, 409 or 422, never 500. A request that answers 500 raises in the TestClient of get_api_client (and so in parallel); to count the statuses of such responses instead, call TestClient(app, raise_server_exceptions=False) in the functions.

Examples

Basic Factory Usage

from bazis_test_utils.factories_abstract import (
    ParentEntityFactoryAbstract,
    ChildEntityFactoryAbstract
)

# Create a single instance
parent = ParentEntityFactory.create(name="Test Parent")

# Create multiple instances
parents = ParentEntityFactory.create_batch(5)

# Create without saving to database
parent = ParentEntityFactory.build()

Testing Hierarchical Relationships

import pytest
from .factories import ParentEntityFactory, ChildEntityFactory

@pytest.mark.django_db
def test_parent_child_relationship():
    parent = ParentEntityFactory.create()
    child = ChildEntityFactory.create(parent=parent)
    
    assert child.parent == parent
    assert child in parent.children.all()

Using with pytest-factoryboy

# conftest.py
from pytest_factoryboy import register
from .factories import ParentEntityFactory, ChildEntityFactory

register(ParentEntityFactory)
register(ChildEntityFactory)

# test_entities.py
@pytest.mark.django_db
def test_with_fixtures(parent_entity, child_entity):
    # Fixtures are automatically created
    assert parent_entity.id is not None
    assert child_entity.id is not None

API Integration Testing

from bazis_test_utils.utils import get_api_client
from .factories import ParentEntityFactory

def test_crud_operations(sample_app):
    client = get_api_client(sample_app)
    
    # Create
    response = client.post("/api/v1/entities/", json={"name": "Test"})
    assert response.status_code == 201
    entity_id = response.json()["id"]
    
    # Read
    response = client.get(f"/api/v1/entities/{entity_id}/")
    assert response.status_code == 200
    
    # Update
    response = client.put(f"/api/v1/entities/{entity_id}/", json={"name": "Updated"})
    assert response.status_code == 200
    
    # Delete
    response = client.delete(f"/api/v1/entities/{entity_id}/")
    assert response.status_code == 204

Usage in Bazis

This package is used in the sample project in the Bazis core to create standard test models. Models in the sample project inherit from bazis-test-utils models, and factories are used to generate test data across all main framework packages.

The typical workflow in Bazis packages:

  1. Define concrete models in the sample app (sample/entity/models.py) by inheriting from abstract base models
  2. Create factories in tests/factories.py using abstract factory classes
  3. Register factories in conftest.py for use across all tests
  4. Use generated fixtures in integration and unit tests

The pytest plugin

Installed, the package is a pytest plugin (entry point pytest11 bazis):

  • the triggers of django-pgtrigger are installed in the test database once it is set up, if pgtrigger is in INSTALLED_APPS (a conftest no longer overrides django_db_setup for them);
  • the roles of bazis-permit (roles.py), the workflows of bazis-statusy (workflow.py) and the schedules of bazis-bg (schedules.py) declared in the code are applied by migrate and flush, so the test database has them; bazis_test_utils.plugin.apply_declarations() applies them again and forgets the cached permissions and content types, and the fixture bazis_declared calls it, for a test that changes the declared rows itself (--reuse-db still runs migrate, which applies them):
def test_the_client_sees_his_tickets(bazis_declared, client_user):
    ...
  • the counts of the request rate limits of Bazis (bazis.core.throttle, kept in Redis between the runs) are reset before each test by throttle_reset() of the core (Bazis 2.16), so a test never inherits a 429 from the tests run before it; within a test the throttles count as usual, so a test of a throttle needs no fixture of its own.

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Run tests (pytest)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Please make sure to:

  • Write tests for new features
  • Update documentation as needed
  • Follow the existing code style
  • Add your changes to the changelog

Development

Setup development environment

# Clone the repository
git clone https://github.com/ecofuture-tech/bazis-test-utils.git
cd bazis-test-utils

# Install dependencies with development extras
uv sync --dev

# Run linting
ruff check .

# Format code
ruff format .

License

Apache License 2.0

See LICENSE file for details.

Support

If you have questions or issues, please:


Made with ❤️ by the Bazis team

Metadata

Release files for bazis-test-utils 2.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bazis-test-utils 2.5.2
File Size Uploaded Provenance Actions
bazis_test_utils-2.5.2.tar.gz 33.8 kB Without provenance Details for bazis_test_utils-2.5.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for bazis-test-utils 2.5.2
File Interpreter ABI Platform Actions
bazis_test_utils-2.5.2-py3-none-any.whl Python 3 none any Details for bazis_test_utils-2.5.2-py3-none-any.whl

Total release size: 59.4 kB

Release files / bazis_test_utils-2.5.2.tar.gz

Download URL bazis_test_utils-2.5.2.tar.gz
Size 33.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b4e07b7ecaa67de7f6ed4a3aa1e33187c0931829f8f2802ca45f911fd82789dd
BLAKE2b-256 checksum
How to use checksums
ba6fd94e3e835a2bbedaaa87facbb188155628761fd499b9f89d681a07111fc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bazis_test_utils-2.5.2-py3-none-any.whl

Download URL bazis_test_utils-2.5.2-py3-none-any.whl
Size 25.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dfc5d36a5faecf1f42136a59df2cf2b533ce7d5aec0840188bd288419a29f050
BLAKE2b-256 checksum
How to use checksums
00a588a213e2914175aa4d854a9ba6fbadce4dc4c8d3dae8f43193a8b071af38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for bazis-test-utils 2.5.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page