pytest bonsai
pytest-bonsai is a plugin that brings elegant, declarative, and composable test data to your test suite.
pytest-bonsai helps you grow minimal, yet expressive dependency trees using Python dataclasses, fixtures, and dynamic parameter resolution.
Installation
$ pip install pytest-bonsai
Showcase
import random
from dataclasses import dataclass, field
from enum import Enum
import pytest
from pytest_bonsai import FixtureRequest, expand, parametrized_fixture
class Rank(Enum):
GENIN = "Genin"
CHUNIN = "Chunin"
JONIN = "Jonin"
class Weapon(Enum):
KATANA = "Katana"
KUSARIGAMA = "Kusarigama"
NUNCHAKU = "Nunchaku"
SHURIKEN = "Shuriken"
TANTO = "Tanto"
@dataclass
class Uniform:
emblem: str
color: str
@dataclass
class Dojo:
name: str
weapons: list[Weapon]
uniform: Uniform
@dataclass
class Ninja:
name: str
rank: Rank
dojo: Dojo
weapon: Weapon
uniform: Uniform
def __repr__(self):
return (
f"{self.name} {self.rank.value}, "
f"armed with {self.weapon.value}, "
f"wearing {self.uniform.color} uniform with {self.uniform.emblem}"
)
# parametrized fixtures act as factories
@dataclass
class DojoParam:
name: str = field(default_factory=lambda: random.choice(["Konoha", "Kiri", "Kumo"]))
weapons: list[Weapon] = field(default_factory=lambda: random.sample(list(Weapon), 2))
uniform_color: str = "black"
@parametrized_fixture(DojoParam)
def dojo(request: FixtureRequest[DojoParam]) -> Dojo:
return Dojo(
name=request.param.name,
weapons=request.param.weapons,
uniform=Uniform(emblem=f"emblem of {request.param.name}-ryu", color=request.param.uniform_color),
)
@dataclass
class NinjaParam:
name: str = field(default_factory=lambda: random.choice(["Hattori", "Goemon", "Kotarou"]))
rank: Rank = field(default_factory=lambda: random.choice(list(Rank)))
dojo: Dojo = field(default_factory=dojo)
weapon: Weapon | None = None
uniform: Uniform | None = None
@parametrized_fixture(NinjaParam)
def ninja(request: FixtureRequest[NinjaParam]) -> Ninja:
return Ninja(
name=request.param.name,
rank=request.param.rank,
dojo=request.param.dojo,
weapon=request.param.weapon or random.choice(request.param.dojo.weapons),
uniform=request.param.uniform or request.param.dojo.uniform,
)
# by default, ninjas use dojo's uniform and weapon
def test_ninja_uses_approved_equipment(ninja, dojo):
assert ninja.weapon in dojo.weapons
assert ninja.uniform is dojo.uniform
# but can have personal preferences
@ninja.parametrize(weapon=Weapon.KATANA)
def test_sword_ninja(ninja, dojo):
assert ninja.weapon == Weapon.KATANA
# some are allowed to wear special uniforms
@pytest.fixture
def red_uniform(dojo):
return Uniform(emblem=dojo.uniform.emblem, color="red")
@ninja.parametrize(uniform=red_uniform)
def test_red_ninja(ninja, dojo):
assert ninja.uniform.color == "red"
# or choose the color on the fly
@ninja.parametrize(uniform=lambda dojo: Uniform(emblem=dojo.uniform.emblem, color="green"))
def test_green_ninja(ninja, dojo):
assert ninja.uniform.color == "green"
# some may even choose a diffrent uniform for each assignment
@parametrized_fixture
def color(request): ...
@ninja.parametrize(uniform=lambda dojo, color: Uniform(emblem=dojo.uniform.emblem, color=color))
@color.parametrize(expand(["blue", "pink"]))
def test_rainbow_ninja(ninja, dojo, color):
assert ninja.uniform.color == color
assert ninja.uniform.emblem == dojo.uniform.emblem
assert ninja.weapon in dojo.weapons
# masters have the highest rank, but specialize in weapon of choice
@parametrized_fixture
def master_weapon(request): ...
@ninja.parametrize(rank=Rank.JONIN, weapon=master_weapon)
class TestMasters:
@master_weapon.parametrize(Weapon.KATANA)
def test_katana_master(self, ninja, master_weapon):
assert ninja.rank == Rank.JONIN
assert ninja.weapon == Weapon.KATANA
@master_weapon.parametrize(Weapon.TANTO)
def test_tanto_master(self, ninja, master_weapon):
assert ninja.rank == Rank.JONIN
assert ninja.weapon == Weapon.TANTO
Metadata
Release files for pytest-bonsai 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_bonsai-0.0.2.tar.gz | 11.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_bonsai-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.6 kB
Release files / pytest_bonsai-0.0.2.tar.gz
| Download URL | pytest_bonsai-0.0.2.tar.gz |
|---|---|
| Size | 11.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fc252f8d1402ef01db1de11d93a90651dbb5c4ba55ef97a556dda9dd24723233
|
|
BLAKE2b-256 checksum How to use checksums |
c30b8dc0459b4b6dd6987f0cc30d891b4750f870eb5d3994d0feab49afecc560
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 8, 2025.
Transparency logRelease files / pytest_bonsai-0.0.2-py3-none-any.whl
| Download URL | pytest_bonsai-0.0.2-py3-none-any.whl |
|---|---|
| Size | 6.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
76784ed09cb038ba46f786aec4e17553b39d74cc68a3f82357cfe14d2ebb08ed
|
|
BLAKE2b-256 checksum How to use checksums |
e528bfff3ae643b284c6ea4ad2bdd10c4ba780edc2c01181cd6f60c39fc5fed9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 8, 2025.
Transparency log