2

I'm seeing duplicate values being generated when using randint and factory_boy. My test fails when checking for the asset for each object. The barcode is also the same between the two when calling create() on the factory.

Do I need to provide a different seed before each object creation?

Packages

  • factory-boy 2.11.1
  • Faker 1.0.4
  • python 2.7
  • django 1.11

Files

seed/factories.py

import factory
from A1.models import *
from random import randint, random
faker = Factory.create()

class AssetFactory(factory.DjangoModelFactory):

    class Meta:
        model = Asset

asset = "ABC" + str(randint(0000, 9999))
barcode = "AA" + randint(111111, 999999)
user = factory.lazy_attribute(lambda x: faker.name())

tests.py

def test_randomCreation(self):
    new_asset = AssetFactory.create()
    new_asset2 = AssetFactory.create()
    self.assertNotEqual(new_asset.asset, new_asset2.asset)  #FAILS

A1/models.py

class Asset(models.Model):
    asset =  models.CharField(max_length=255, verbose_name="Asset")
    barcode = models.CharField(max_length=255)
    user = models.CharField(max_length=255)

May someone point me in the right direction? Thanks in advance!!

Aero Chocolate
  • 1,477
  • 6
  • 23
  • 39

1 Answers1

2

The way the Python language is designed, your code cannot work.

It is strictly equivalent to the following:

_RANDOM_ASSET = "ABC" + str(randint(0000, 9999))
_RANDOM_BARCODE = "AA" + str(randint(111111, 999999))

class AssetFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Asset
    asset = _RANDOM_ASSET
    barcode = _RANDOM_BARCODE
    user = factory.LazyAttribute(lambda x: faker.name())

You've fixed a random value for asset and barcode, so every object generated through that factory will have the exact same value.

The proper way would be to use the various built-in declarations:

class AssetFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Asset

    asset = factory.fuzzy.FuzzyText(length=4, prefix='ABC', chars=string.digits)
    barcode = factory.fuzzy.FuzzyText(length=6, prefix='AA', chars=string.digits)
    user = factory.Faker('name')
Xelnor
  • 3,194
  • 12
  • 14
  • Thanks! I also tried the following and it worked: barcode = factory.LazyAttribute(lambda x: "ABC" + str(randint(111111, 999999)) However, I'm not sure if I should be using it this way. Is there a benefit to doing either way? Note from https://factoryboy.readthedocs.io/en/latest/fuzzy.html?highlight=FuzzyAttribute "LazyAttribute calls the function with the object being constructed as an argument" – Aero Chocolate Mar 21 '19 at 14:59
  • 1
    @AeroChocolate the main advantage of going through `FuzzyText` is that it uses a "managed" random seed; that's important if you want to be able to reproduce random-based tests. See https://factoryboy.readthedocs.io/en/latest/reference.html#randomness-management for details. – Xelnor Mar 28 '19 at 15:21