|
from typing import Dict, List, Tuple |
|
|
|
from hypothesis import HealthCheck, assume, given, settings |
|
from hypothesis.strategies import ( |
|
SearchStrategy, |
|
composite, |
|
data, |
|
deferred, |
|
fixed_dictionaries, |
|
from_type, |
|
just, |
|
one_of, |
|
register_type_strategy, |
|
tuples, |
|
) |
|
from pydantic import BaseModel |
|
from pydantic.dataclasses import dataclass |
|
|
|
|
|
@composite |
|
def dataclass_instances(draw, cls=None): |
|
if cls is None: |
|
raise ValueError("No class provided to dataclass strategy") |
|
|
|
def recursive_generate(t): |
|
if hasattr(t, "__annotations__"): |
|
return fixed_dictionaries( |
|
{ |
|
attribute_name: recursive_generate(attribute_type) |
|
for attribute_name, attribute_type in t.__annotations__.items() |
|
} |
|
) |
|
else: |
|
return from_type(t) |
|
|
|
return cls(**draw(recursive_generate(cls))) |
|
|
|
|
|
hashable_types = one_of( |
|
just(int), |
|
just(str), |
|
just(float), |
|
deferred(lambda: hashable_types.map(lambda t: Tuple[t])), |
|
deferred( |
|
lambda: tuples(hashable_types, hashable_types).map( |
|
lambda t_u: Tuple[t_u[0], t_u[1]] |
|
) |
|
), |
|
deferred( |
|
lambda: tuples(hashable_types, hashable_types, hashable_types).map( |
|
lambda t_u_r: Tuple[t_u_r[0], t_u_r[1], t_u_r[2]] |
|
) |
|
), |
|
) |
|
|
|
|
|
@composite |
|
def pydantic_dataclasses( |
|
draw, a_value=deferred(lambda: valid_types), b_value=deferred(lambda: valid_types) |
|
): |
|
@dataclass |
|
class DataClassExample: |
|
a: draw(a_value) |
|
b: draw(b_value) |
|
|
|
return DataClassExample |
|
|
|
|
|
@composite |
|
def pydantic_models( |
|
draw, a_value=deferred(lambda: valid_types), b_value=deferred(lambda: valid_types) |
|
): |
|
class ModelExample(BaseModel): |
|
a: draw(a_value) |
|
b: draw(b_value) |
|
|
|
register_type_strategy(ModelExample, dataclass_instances) |
|
return ModelExample |
|
|
|
|
|
valid_types: SearchStrategy = one_of( |
|
hashable_types, |
|
deferred(lambda: valid_types.map(lambda inner: List[inner])), |
|
deferred( |
|
lambda: tuples(hashable_types, valid_types).map( |
|
lambda args: Dict[args[0], args[1]] |
|
) |
|
), |
|
pydantic_dataclasses(), |
|
pydantic_models(), |
|
) |
|
|
|
|
|
@settings(suppress_health_check=(HealthCheck.too_slow,)) |
|
@given(data=data(), target=one_of(pydantic_dataclasses(), pydantic_models())) |
|
def test_pydantic_schemas_can_be_generated(data, target): |
|
assert isinstance(data.draw(dataclass_instances(target)), target) |
|
|
|
|
|
@settings(suppress_health_check=(HealthCheck.too_slow,)) |
|
@given(data=data(), target=pydantic_dataclasses(b_value=pydantic_models())) |
|
def test_nested_schemas_can_be_generated(data, target): |
|
assert isinstance(data.draw(dataclass_instances(target)), target) |
|
|
|
|
|
@settings(suppress_health_check=(HealthCheck.too_slow,)) |
|
@given(data=data(), target=pydantic_dataclasses()) |
|
def test_lists_of_dataclasses_can_be_generated(data, target): |
|
generated = data.draw(from_type(List[target])) |
|
assume(generated) |
|
assert isinstance(generated[0], target) |
|
|
|
|
|
@settings(suppress_health_check=(HealthCheck.too_slow,)) |
|
@given(data=data(), target=pydantic_models()) |
|
def test_lists_of_models_can_be_generated(data, target): |
|
generated = data.draw(from_type(List[target])) |
|
assume(generated) |
|
assert isinstance(generated[0], target) |
Something weird is going on with pydantic and inheritance. You need to register
ModelExample