Search code examples
pythondjangodjango-testingdjango-testsfactory-boy

Populate Specific Database With factory_boy Data for testing


I have an application that has some unmanaged models that pull from a table in a different DB.

I've created Factories for all my models and in most cases factory_boy has worked great!

The problem I'm running into has to do with a specific unmanaged model that uses a manager on one of the fields.

class DailyRoll(StaffModel):
    id            = models.IntegerField(db_column='ID', primary_key=True)
    location_id   = models.IntegerField(db_column='locationID')
    grade         = models.CharField(db_column='grade', max_length=10)
    end_year      = models.IntegerField(db_column='endYear')
    run_date      = models.DateField(db_column='runDate')
    person_count = models.IntegerField(db_column='personCount')
    contained_count = models.IntegerField(db_column='containedCount')
    double_count = models.IntegerField(db_column='doubleCount')
    aide_count = models.IntegerField(db_column='aideCount')
    one_count = models.IntegerField(db_column='oneCount')
    bi_count = models.IntegerField(db_column='biCount')
    last_run_date = managers.DailyRollManager()

    class Meta(object):
        managed  = False
        db_table = '[DATA].[position_dailyRoll]'

The manager looks like this:

class DailyRollManager(models.Manager):
    def get_queryset(self):
        last_run_date = super().get_queryset().using('staff').last().run_date
        return super().get_queryset().using('staff').filter(
            run_date=last_run_date
        )

My factory_boy setup looks like this (it's very basic at the moment, because I'm just trying to get it to work):

class DailyRollFactory(factory.Factory):
    class Meta:
        model = staff.DailyRoll

    id = 1
    location_id = 1
    grade = 1
    end_year = 2019
    run_date = factory.fuzzy.FuzzyDateTime(timezone.now())
    person_count = 210
    contained_count = 1
    double_count = 2
    aide_count = 3
    one_count = 4
    bi_count = 5
    last_run_date = managers.DailyRollManager()

I'm sure the last_run_date is setup improperly - but I don't know how best to handle using a manager in factory_boy.

When I run my tests - it creates a default sqlite3 database named 'defaultdb' to run tests against (we use SQL Server in production - but because of security we cannot give django control over master to manage testing). To accomplish this I had to create a workaround, but it works.

My settings look like this:

if 'test' in sys.argv or 'test_coverage' in sys.argv:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'defaultdb.sqlite3'),
        },
        'staff': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'staff.sqlite3'),
        },
    }

My test is simple like this:

def test_index_returns_200_and_correct_template(self):
    """
    index page returns a 200
    """
    # Load test data which index needs
    # if this is not included the test fails with
    # 'no such table: [DATA].[position_dailyRoll]'
    f.DailyRollFactory()
    response = self.client.get(reverse('position:index'))
    self.assertEqual(response.status_code, 200)
    self.assertTemplateUsed(response, 'position/index.html')

If I don't include f.DailyRollFactory() in the test it fails with this error:

no such table: [DATA].[position_dailyRoll]

When I include f.DailyRollFactory() it gives this error:

TypeError: 'last_run_date' is an invalid keyword argument for this function

I think that ultimately that manager is trying to look at the 'staff' database - which I have being created, but it's empty and contains no data.

I am trying to figure out:

A) How can I pre-populate that staff database with data like what I have in my factory-boy model? I think that may resolve the issue I'm running into. Will it? I am not sure.

B) Is that the right way to handle the manager in a factory_boy Factory? I'm not sure exactly how to handle that.


Solution

  • I ended up using Pytest-Django as that testing framework actually allows for doing exactly what I wanted.

    Using the --nomigrations flag, it takes my models which are only managed by django in tests and creates the appropriate table name for them (using their db_table attribute) in the test database. Then I can use factory_boy to create mock data and test it up!