litestar-org / polyfactory

Simple and powerful factories for mock data generation
https://polyfactory.litestar.dev/
MIT License
988 stars 78 forks source link

Bug: Custom Random instance passed to __random__ does not get passed to list/set fields #553

Closed TheGrandmother closed 1 week ago

TheGrandmother commented 3 months ago

Description

I ham having issues running polyfactory with the property based testing library hypothesis. Hypothesis will get angry and throw warnings if one calls the random module from within a test or strategy as this breaks some aspect of hypothesis.

But hypothesis provides a method for getting an instance of the Random and just supplying that to the __random__ field on a polyfactory class solves the issue.

But, lo and behold, out of somewhere the warning started popping up again. After some dissection i found that it showed up in models that had fields with collections like list or set. It appears that the instance of Random is not propagated to the factory for the "inner type"/argument for the collection.

URL to code causing the issue

No response

MCVE

class A(BaseModel):
    x: str

class B(BaseModel):
    x: list[A]

class C(BaseModel):
    x: A

@st.composite
def factory_strat(draw):
    class Factory(ModelFactory[B]):
        __random__ = draw(st.randoms()) # Returns a nice instance of Random

    return Factory.build()

@given(factory_strat())
def test_something(f):
    pass

Steps to reproduce

When ran in pytest with hypothesis warnings will be emitted that the random module is being used.

If I let the factory extend A or C instead the warning does not get emitted as polyfactory appears to be using the special Random instance generated by st.randoms.

Screenshots

No response

Logs

No response

Release Version

2.16.0

Platform


[!NOTE]
While we are open for sponsoring on GitHub Sponsors and OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.

Fund with Polar

adhtruong commented 2 months ago

Thanks for the detailed report! Really helpful. I'm able to reproduce with

from hypothesis import given
from hypothesis import strategies as st
from pydantic import BaseModel

from polyfactory.factories.pydantic_factory import ModelFactory

class A(BaseModel):
    x: str

class B(BaseModel):
    x: list[A]

class C(BaseModel):
    x: A

@st.composite
def factory_strat(draw):
    class Factory(ModelFactory[B]):
        __random__ = draw(st.randoms())  # Returns a nice instance of Random

    return Factory.build()

@given(factory_strat())
def test_something(f: B):
    pass

PRs welcome to fix.