factory_boy icon indicating copy to clipboard operation
factory_boy copied to clipboard

How to randomly null fields

Open alicederyn opened this issue 4 years ago • 7 comments

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?

alicederyn avatar Aug 28 '19 12:08 alicederyn

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]
  1. 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

francoisfreitag avatar Aug 28 '19 12:08 francoisfreitag

@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:

rbarrois avatar Aug 31 '19 15:08 rbarrois

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 avatar Aug 29 '20 13:08 grondman

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.

grondman avatar Aug 29 '20 14:08 grondman

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')),
)

rbarrois avatar Aug 31 '20 12:08 rbarrois

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...

FractalWire avatar Dec 30 '20 13:12 FractalWire

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

rbarrois avatar Dec 30 '20 17:12 rbarrois