FactoryBoy / factory_boy

A test fixtures replacement for Python
https://factoryboy.readthedocs.io/
MIT License
3.48k stars 392 forks source link

How to randomly null fields #646

Open alicederyn opened 5 years ago

alicederyn commented 5 years ago

I'm probably missing something obvious, but I've been through the documentation a few times, and I can't find a way to sometimes assign None to a nullable field. (My specific case is a field that is sometimes a date and sometimes empty.) What is the correct way to do this? Is it in the documentation?

francoisfreitag commented 5 years ago

I do not think there’s a built-in way to make None values appear for any given factory. Tests are easier to understand and maintain when they use deterministic or predictable values.

A couple idea if you really want to generate None:

  1. use factory.Iterator with an iterable that contains None
    [datetime.date(2019, 1, 1), datetime.date(2019, 2, 1), None]
  2. use factory.LazyFunction
    date = factory.LazyFunction(lambda: datetime.date.today() if random.random() > 0.3 else None)

For future reference, please try to keep issue for issues with Factory Boy and post to stack overflow to get help on its usage. https://stackoverflow.com/questions/tagged/factory-boy

rbarrois commented 4 years ago

@alicederyn does that solve your issue?

If you feel that this kind of examples should appear in the docs, we'll be very happy to welcome new contributions (for instance in the recipes section?) :wink:

grondman commented 3 years ago

I'd like to see this popping up in the documentation somewhere. I'm a Django developer and use FactoryBoy in combination with Faker to generate large sets of mock data. Especially for fields that are allowed to be be blank/null I think it's vital to be able to have sometimes have those fields randomly set to None by FactoryBoy (or Faker).

For example: if I allow someone to (optionally) upload a profile picture, I should be able to generate both None and random images. I'm trying this now by using

avatar = LazyFunction(lambda: ImageField(width=500, height=200, color=Faker("color")) if random() > .5 else None)

but calling the factory using this field then fails with

AttributeError: 'ImageField' object has no attribute '_committed'
grondman commented 3 years ago

Adding to my previous comment, I'm now using the solution suggested at https://stackoverflow.com/questions/60957904/optional-nullable-value-for-factory-from-factory-boy which works, so I'm not touching my factory anymore :)

Still wondering what the developers of Factory Boy think would be the best way to go here, though.

rbarrois commented 3 years ago

Oh, looks like I missed that question :)

The simplest would be:

avatar = factory.Maybe(
    factory.Faker('pybool'),
    factory.ImageField(width=500, height=200, color=factory.Faker('color')),
)
FractalWire commented 3 years ago

The doc does not highlight the fact that the decider of Maybe could be an arbitrary value. In the doc, it looks like it must be a field name.

I also did not get that the yes_declaration or no_declaration were optional...

rbarrois commented 3 years ago

@FractalWire that's a good point; I guess the doc could be improved there. Time is always the issue there :D